1. 程式人生 > >QT學習(五)QT之多執行緒

QT學習(五)QT之多執行緒

1.  執行緒同時進行

QT提供了QThread來定義一個執行緒,我們通過定義類thread來重新實現它。

classThread:publicQThread

{

    Q_OBJECT

 

public:

    Thread();

 

    voidsetMessage(constQString&Message);

    voidstop();

 

protected:

   

voidrun();

 

private:

    QStringMessageStr;

    volatileboolstopped;

 

 

};

執行緒通過判斷stopped變數來選擇列印資訊,重新實現了run函式和stop函式,用於執行緒執行和終止。

voidThread::run(){
    while(!stopped)
        std::cerr<<qPrintable
(MessageStr);
    stopped=false;
    std::cerr<<std::endl;
 
}
voidThread::stop(){
    stopped=true;
}

接下來製作一個簡單的對話方塊,實現兩個按鈕,可以呼叫執行緒。這兩個執行緒可以同時執行,互不相關,有各自的stopped判別變數。

public:
    threadDialog(QWidget*parent=0);
 
privateslots:
    voidstartOrStopThreadA();
    voidstartOrStopThreadB();
protected:
    voidcloseEvent(QCloseEvent*closeEvent);
 
private:
    ThreadthreadA;
    ThreadthreadB;
    QPushButton*threadButtonA;
    QPushButton*threadButtonB;
    QPushButton*quitButton;

這是類的定義,包括定義了三個按鈕,分別用於啟動執行緒A和B以及停止兩個執行緒的按鈕。


當同時按下A 和B執行緒的按鈕,會交替輸出A和B的資訊。

2.  互斥鎖

QT通過QMutex來建立保護多執行緒共同訪問區,通過lock和unlock分別來鎖住和解鎖一段變數或者程式碼。當引用lock時,其後邊的程式碼都被當前程序佔用,其他程序不能訪問,直到呼叫unlock後,其他程式才可以呼叫。上面程式碼可以修改為:

voidThread::run(){
    forever{
        mutex.lock();
        if(stopped){
            stopped=false;
            mutex.unlock();
            break;
        }
        mutex.unlock();
        std::cerr<<qPrintable(MessageStr);
    }
    std::cerr<<std::endl;
}
 
 
voidThread::stop(){
    mutex.lock();
    stopped=true;
    mutex.unlock();
}

3.  訊號量

訊號量可以用於當兩個執行緒之間傳遞大量資料時,兩個執行緒通過訊號量來確定緩衝區大小,以及自己是否可以進行讀寫操作。讀寫緩衝區是典型的生產者和消費者型別,生產者向緩衝區寫資料,消費者讀取資料。現在定義兩個訊號量,一個檢測緩衝區可以寫的空間大小,一個記錄可以讀的空間大小,這類似於FIFO。

QSemaphorefreeSpace(BufferSize);

QSemaphoreusedSpace(0);

訊號量通過acquire來獲得可用區域,而通過release來釋放已經完成的區域。

voidProducer::run()
{
    for(inti=0;i<DataSize;++i){
        freeSpace.acquire();
        buffer[i%BufferSize]="ACGT"[uint(std::rand())%4];
        usedSpace.release();
    }
}
voidConsumer::run()
{
    for(inti=0;i<DataSize;++i){
        usedSpace.acquire();
        std::cerr<<buffer[i%BufferSize];
        freeSpace.release();
    }
    std::cerr<<std::endl;
}

生產者每次寫入一個數據,usedSpace就增加一個可讀資料。而消費者每次讀一個數據,freeSpace就增加一個自由空間。

4.  等待條件

除了使用訊號量,還可以通過QWaitCondition定義等待變數,用於執行緒開啟和等待。

QWaitConditionbufferIsNotFull;

QWaitConditionbufferIsNotEmpty;

QMutexmutex;

intusedSpace=0;

以上bufferIsNotFull和bufferIsNotEmpty可以來喚起和阻塞程序。而usedSpace則用於計算可用空間。

voidProducer::run()
{
    for(inti=0;i<DataSize;++i){
        mutex.lock();
        while(usedSpace==BufferSize)
            bufferIsNotFull.wait(&mutex);
        buffer[i%BufferSize]="ACGT"[uint(std::rand())%4];
        ++usedSpace;
        bufferIsNotEmpty.wakeAll();
        mutex.unlock();
    }
}

 

 

voidConsumer::run()
{
    for(inti=0;i<DataSize;++i){
        mutex.lock();
        while(usedSpace==0)
            bufferIsNotEmpty.wait(&mutex);
        std::cerr<<buffer[i%BufferSize];
        --usedSpace;
        bufferIsNotFull.wakeAll();
        mutex.unlock();
    }
    std::cerr<<std::endl;
}

 

生產者如果檢測到空間已經寫滿,就會被bufferIsNotFull阻塞等待,直到消費者通過消費空間,使得usedSpace不滿為止。然後生產者寫入一個數據,可用空間就增加一個。消費者正好與生產者相反。