mutex一般稱為互斥鎖,是用於線程同步的。Qt幫助文檔對QMutex有一段描述:QMutex是為了保護一個對象、數據結構或代碼段,在同一個時刻只能有一個線程能訪問它。我覺得這句話很容易誤導人,看這句話會把關注點放在對象、數據結構或代碼段上。但是個人覺得QMutex重點應該放在QMutex與線程的關系上。直接通過例子來看一看。
a.不使用QMutex的多線程運行情況。
1.新建一個類Thread,繼承於QThread,重寫run函數。
void Thread::run() { qDebug()<<"第一句話"<<QThread::currentThreadId(); qDebug()<<"第二句話"<<QThread::currentThreadId(); qDebug()<<"第三句話"<<QThread::currentThreadId(); }
run函數里執行了三個操作,都是打印。
2.創建兩個Thread對象並start。這樣就會有兩個線程。
Thread t1; Thread t2; t1.start(); t2.start();
這里我們沒有使用QMutex,看看運行情況。
從結果可以看到兩個線程的三個操作是交替執行(也有可能是別的情況)。
b.加上QMutex。
3.在run函數中加上QMutex.
QMutex mutex; //全局的對象 void Thread::run() { mutex.lock(); qDebug()<<"第一句話"<<QThread::currentThreadId(); qDebug()<<"第二句話"<<QThread::currentThreadId(); qDebug()<<"第三句話"<<QThread::currentThreadId(); mutex.unlock(); }
運行情況如下:
從結果可以看出,先執行完第一個線程的三個操作,再執行第二個線程的操作。感覺好像是mutex鎖住了那三個qDebug操作。
c.再來一個不加鎖的線程。
4.新建一個類Thread2,繼承於QThread,重寫run函數。
void Thread2::run() { qDebug()<<"第一句話"<<QThread::currentThreadId()<<"--thread2--"; qDebug()<<"第二句話"<<QThread::currentThreadId()<<"--thread2--"; qDebug()<<"第三句話"<<QThread::currentThreadId()<<"--thread2--"; }
為了區分,qDebug最后加上了“--thread2--”
5.創建Thread2類的對象,並start
Thread t1; Thread t2; Thread2 t3; //Thread2對象.. t1.start(); t2.start(); t3.start();
查看運行結果:
從結果可以看到原來的一個線程的三句話沒有連續打印,這樣看好像mutex並沒有鎖住三個qDebug的操作。但是如果把Thread2的線程打印結果去掉,另外兩個線程的結果還是按順序執行的,所以說mutex是起作用的。
那QMutex的作用該怎么理解呢? 假如把QMutex比作是一個標簽,它有兩個狀態:使用中和未使用。從上面的例子看
-
mutex在第一個線程(t1)中標記為使用中(lock操作)。
-
這時第二個線程也想要標記(lock),但是mutex已經被標記為使用中了,所以他只能等,之道t1把mutex標記為未使用(unlock)。在等待的期間t2中lock以下的操作都沒有被執行。所以會看到t1的三句話按順序出來了。
-
第三個線程t3根本就不管另外兩個線程,老子自己執行自己的(它沒有調用lock,所以沒有被鎖住)。該在什么時刻運行就什么時候運行。
綜上,如果還要讓三句話按順序執行,還需要在t3上加上那把鎖:
void Thread2::run() { mutex.lock(); qDebug()<<"第一句話"<<QThread::currentThreadId()<<"--thread2--"; qDebug()<<"第二句話"<<QThread::currentThreadId()<<"--thread2--"; qDebug()<<"第三句話"<<QThread::currentThreadId()<<"--thread2--"; mutex.unlock(); }
上一篇說了那么多就是想表達QMutex是怎么運行的。不過QMutex的目的是保護數據,接下來就看看QMutex保護數據的例子吧。
例:
從QThread派生兩個類Thread和Thread2,兩個類的run函數分別如下:
int number = 0; //全局變量 void Thread::run() { number += 5; qDebug()<<"---------------"; int val = number*3; qDebug()<<"thread1"<<val; } void Thread2::run() { number += 3; qDebug()<<"---------------"; int val = number*2; qDebug()<<"therad2"<<val; }
兩個線程的工作就是使用全局變量number來計算獲得最終結果。若Thread線程先執行那么預期的結果將是Thread輸出15。但是運行結果卻是:
24是怎么來的呢?number在Thread線程中+=5變為了5,然后在Thread2線程中+=3變為了8,所以在Thread線程中再計算val的值時number已經變為了8,結果就是24了。
(說明:兩個run函數中都有加qDebug()<<"---------------",目的是讓兩個線程交替執行,不然有可能線程在一個時間片內就完成了計算,結果就是預期的了。所以此處這個qDebug純粹是為了寫這個反例,沒有實際意義。)
這時候就需要QMutex上場了,根據上一篇所說的,我們需要在兩個線程中都加上QMutex。
void Thread::run() { mutex.lock(); number += 5; qDebug()<<"---------------"; int val = number*3; qDebug()<<"thread1"<<val; mutex.unlock(); } void Thread2::run() { mutex.lock(); number += 3; qDebug()<<"---------------"; int val = number*2; qDebug()<<"therad2"<<val; mutex.unlock(); }
運行結果:
QMutex雖好,但使用時也要小心一點,有lock就要unlock。不然的話別的線程就慘了。比如說把Thread::run中的mutex.unlock注釋掉。那么運行結果就是:
只有Thread的結果打印出來了,那是因為Thread2還卡在mutex.lock這里,它還在問mutex你解鎖了沒.....
轉自:https://www.fearlazy.com/index.php/post/97.html
https://www.fearlazy.com/index.php/post/98.html