瞧一瞧Qt的QMutex


    mutex一般稱為互斥鎖,是用於線程同步的。Qt幫助文檔對QMutex有一段描述:QMutex是為了保護一個對象、數據結構或代碼段,在同一個時刻只能有一個線程能訪問它。我覺得這句話很容易誤導人,看這句話會把關注點放在對象、數據結構或代碼段上。但是個人覺得QMutex重點應該放在QMutex與線程的關系上。直接通過例子來看一看。

    a.不使用QMutex的多線程運行情況。

    1.新建一個類Thread,繼承於QThread,重寫run函數。

C++
void Thread::run() { qDebug()<<"第一句話"<<QThread::currentThreadId(); qDebug()<<"第二句話"<<QThread::currentThreadId(); qDebug()<<"第三句話"<<QThread::currentThreadId(); }

    run函數里執行了三個操作,都是打印。

    2.創建兩個Thread對象並start。這樣就會有兩個線程。

C++
    Thread t1; Thread t2; t1.start(); t2.start();

    這里我們沒有使用QMutex,看看運行情況。

    從結果可以看到兩個線程的三個操作是交替執行(也有可能是別的情況)。

   b.加上QMutex。

   3.在run函數中加上QMutex.

C++
 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函數。

C++
void Thread2::run() { qDebug()<<"第一句話"<<QThread::currentThreadId()<<"--thread2--"; qDebug()<<"第二句話"<<QThread::currentThreadId()<<"--thread2--"; qDebug()<<"第三句話"<<QThread::currentThreadId()<<"--thread2--"; }

為了區分,qDebug最后加上了“--thread2--”

 5.創建Thread2類的對象,並start

C++
    Thread  t1; Thread t2; Thread2 t3; //Thread2對象.. t1.start(); t2.start(); t3.start();

查看運行結果:

從結果可以看到原來的一個線程的三句話沒有連續打印,這樣看好像mutex並沒有鎖住三個qDebug的操作。但是如果把Thread2的線程打印結果去掉,另外兩個線程的結果還是按順序執行的,所以說mutex是起作用的。

 

那QMutex的作用該怎么理解呢? 假如把QMutex比作是一個標簽,它有兩個狀態:使用中和未使用。從上面的例子看

  1. mutex在第一個線程(t1)中標記為使用中(lock操作)。

  2. 這時第二個線程也想要標記(lock),但是mutex已經被標記為使用中了,所以他只能等,之道t1把mutex標記為未使用(unlock)。在等待的期間t2中lock以下的操作都沒有被執行。所以會看到t1的三句話按順序出來了。

  3. 第三個線程t3根本就不管另外兩個線程,老子自己執行自己的(它沒有調用lock,所以沒有被鎖住)。該在什么時刻運行就什么時候運行。

 

綜上,如果還要讓三句話按順序執行,還需要在t3上加上那把鎖:

C++
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函數分別如下:

C++
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。

C++
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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM