Qt同步線程的幾種方法


一、QMutex類

QMutex類就像一把鎖,在互斥量之前上鎖(QMutex::lock()),然後在使用完互斥量之後解鎖(QMutex::unlock())。比如下面的代碼:

void someMethod()
{
    mutex.lock();
    qDebug()<<"Hello";
    qDebug()<<"World";
    mutex.unlock();
}

class Thread1 : public QThread  
{
protected:
    void run()
    {
        someMethod();
    }
};

class Thread2 : public QThread  
{
protected:
    void run()
    {
        someMethod();
    }
};
如上面的代碼,在函數someMethod裏面有兩條語句,如果有兩個線程啓動之後,這兩個線程都將調用這個函數(run函數即爲線程啓動後執行的程序),則可能會出現的結果是Hello Hello World World。但是這並不是我們想要的,我們希望的是每個線程可以一次性執行完someMethod函數裏面的代碼。這個時候我們便可以在函數倆面給函數體加上鎖,然後在結束的時候解鎖。
這裏需要注意的是,如果一個線程試圖向一個已經被其它線程上鎖了互斥量上鎖的話,這個線程將被阻塞,直到這個互斥量被解鎖。如果一個線程希望自己在試圖對一個上鎖了的互斥量進行訪問的時候能夠不被阻塞,可以將lock()函數替換爲tryLock()函數,這個函數的效果是:如果線程正在試圖訪問的互斥量已經被上鎖了,那麼可以立即返回而不被阻塞。

二、QMutexLocker便利類

使用QMutex對互斥量進行加鎖解鎖比較繁瑣,在一些複雜的函數或者拋出C++異常的函數中都非常容易發生錯誤。可以使用一個方便的QMutexLocker類來簡化對互斥量的處理。首先,QMutexLocker類的構造函數接收一個QMutex對象作爲參數並且上鎖,然後在析構函數中自動對其進行解鎖。如下代碼:
QMutex mutex;

void someMethod()
{
    QMutexLocker locker(&mutex);
    qDebug()<<"Hello";
    qDebug()<<"World";
}
這裏創建一個QMutexLocker類實例,在這個實例的構造函數中將對mutex對象進行加鎖。然後在析構函數中自動對mutex進行解鎖。解鎖的工作不需要顯示地調用unlock函數,而是根據QMutexLocker對象的作用域綁定在一起了。

三、QReadWriteLock類

前兩種保護互斥量的方法比較絕對,其達到的效果是:不管我要對互斥量做些是什麼,我都要一個人霸佔着,即使我只是看看它,也不能讓別人看。這會使得這個互斥量資源的使用率大大下降,造成資源等待等問題。於是,我們可以對線程對互斥量的操作進行分類:讀和寫。有幾種情況:1、如果我只是看看的話,你也可以看,大家看到的都是正確的;2、如果我要看這個數據,你是不能改的,不然我看到的就不知道是什麼了;3、我在改的時候,你不能看的,我可能會讓你看到不正確的了;4、我在改的時候,你當然不能改了。
因此,我們可以對QMutex鎖進行升級,將其升級爲QReadWriteLock,QMutex加鎖的方法是lock(),而QReadWriteLock鎖有兩種鎖法:設置爲讀鎖(lockForRead())和寫鎖(lockForWrite())。代碼如下:

QReadWriteLock lock;
void someMethod()
{
lock.lockForRead();
//lock.lockForWrite();
qDebug()<<"Hello";
qDebug()<<"World";

lock.unlock();
}


於是可能有一下三種情況:1、一個線程試圖對一個加了讀鎖的互斥量進行上讀鎖,允許;2、一個線程試圖對一個加了讀鎖的互斥量進行上寫鎖,阻塞;3、一個線程試圖對一個加了寫鎖的互斥量進行上讀鎖,阻塞;一個線程試圖對一個加了寫鎖的互斥量進行上寫鎖,阻塞。
所以可以看出,讀寫鎖比較適用的情況是:需要多次對共享的數據進行讀操作的閱讀線程。

四、QReadLocker便利類和QWriteLocker便利類對QReadWriteLock進行加解鎖

和QMutex與QMutexLocker類的關係類似,關於讀寫鎖也有兩個便利類,讀鎖和寫鎖,QReadLocker和QWriteLocker。它們的構造函數都是一個QReadWriteLock對象,不同的是,在QReadLocker的構造函數裏面是對讀寫鎖進行lockForRead()加鎖操作,而在QWriteLocker的構造函數裏面是對讀寫鎖進行lockForWrite()加鎖操作。然後解鎖操作unlock()都是在析構函數中完成的。

五、信號量QSemaphore

前面的幾種鎖都是用來保護只有一個量的互斥量的。但是還有些互斥量(資源)的數量並不止一個,比如一個電腦安裝了2個打印機,我已經申請了一個,但是我不能霸佔這兩個,你來訪問的時候如果發現還有空閒的仍然可以申請到的。於是這個互斥量可以分爲兩部分,已使用和未使用。一個線程在申請的時候,會對未使用到的部分進行加鎖操作,如果加鎖失敗則阻塞,如果加鎖成功,即又有一個資源被使用了,於是則將已使用到的部分解鎖一個。以著名的生產者消費者問題爲例,分析問題:生產者需要的是空閒位置存放產品,結果是可取的產品多了一個。於是,我們可以定義兩個信號量:QSemaphore freeSpace和QSemaphore usedSpace,前者是給生產者使用的,後者是給

六、條件觸發QWaitCondition(未完待續、、、)

發佈了68 篇原創文章 · 獲贊 42 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章