Qt多線程編程總結(二)——QMutex


QMutex類提供的是線程之間的訪問順序化。

 

QMutex的目的是保護一個對象、數據結構或者代碼段,所以同一時間只有一個線程可以訪問它。(在Java術語中,它和同步關鍵字“synchronized”很相似)。例如,這里有一個方法打印給用戶兩條消息:

 

[cpp]  view plain  copy
 
  1. void someMethod()  
  2. {  
  3.    qDebug("Hello");  
  4.    qDebug("World");  
  5. }  

 

如果同時在兩個線程中調用這個方法,結果的順序將是:

 

  Hello
  Hello
  World
  World
  

如果你使用了一個互斥量:

 

[cpp]  view plain  copy
 
  1. QMutex mutex;  
  2.   
  3. void someMethod()  
  4. {  
  5.    mutex.lock();  
  6.    qDebug("Hello");  
  7.    qDebug("World");  
  8.    mutex.unlock();  
  9. }  

 

用Java的術語,這段代碼應該是:

 

[cpp]  view plain  copy
 
  1. void someMethod()  
  2. {  
  3.    synchronized {  
  4.      qDebug("Hello");  
  5.      qDebug("World");  
  6.    }  
  7. }  

 

 

然后同一時間只有一個線程可以運行someMethod並且消息的順序也一直是正確的。當然,這只是一個很簡單的例子,但是它適用於任何需要按特定頻率發生的情況。

但你在一個線程中調用lock(),其它線程將會在同一地點試圖調用lock()來阻塞,知道這個線程調用unlock()之后其它線程才會獲得這個鎖。lock()的一種非阻塞選擇是tryLock()。

實驗部分:

情形一:

 

[cpp]  view plain  copy
 
  1. #include <QtCore/QCoreApplication>  
  2. #include <Qthread>  
  3. #include <QTextStream>  
  4. class MyThreadA : public QThread {    
  5. public:      
  6. virtual void run();    
  7. };    
  8. class MyThreadB: public QThread {    
  9. public:      
  10. virtual void run();    
  11. };  
  12. int number=6;  
  13. void MyThreadA::run(){    
  14. number *= 5;  
  15. number /= 4;  
  16. }    
  17. void MyThreadB::run(){  
  18. number *= 3;  
  19. number /= 2;  
  20. }   
  21. int main(int argc, char *argv[])  
  22. {     
  23. QCoreApplication app(argc, argv);  
  24. MyThreadA a;    
  25. MyThreadB b;    
  26. a.run();  
  27. b.run();  
  28.  a.terminate();  
  29. b.terminate();  
  30. QTextStream out(stdout);  
  31. out<<number;  
  32. return app.exec();  
  33. }   

 

上述代碼,很簡單,寫了兩個線程,覆蓋了QThread的純虛函數run(),這兩個重構的run方法都是對全局變量number的操作,

主函數中順序調用這兩個方法,a.run()執行后number為7,b.run()執行后為10。

情形二:

 

[cpp]  view plain  copy
 
  1. #include <QtCore/QCoreApplication>  
  2. #include <Qthread>  
  3. #include <QTextStream>  
  4. class MyThreadA : public QThread {    
  5. public:      
  6. virtual void run();    
  7. };    
  8. class MyThreadB: public QThread {    
  9. public:      
  10. virtual void run();    
  11. };  
  12. int number=6;  
  13. void MyThreadA::run(){    
  14. number *= 5;  
  15. sleep(1);  
  16. number /= 4;  
  17. }    
  18. void MyThreadB::run(){  
  19. number *= 3;  
  20. sleep(1);  
  21. number /= 2;  
  22. }   
  23. int main(int argc, char *argv[])  
  24. {     
  25. QCoreApplication app(argc, argv);  
  26. MyThreadA a;    
  27. MyThreadB b;    
  28. a.start();  
  29. b.start();    
  30. a.wait();    
  31. b.wait();   
  32. QTextStream out(stdout);  
  33. out<<number;  
  34. return app.exec();  
  35. }   

 

 

運行結果:

number=11;

利用QThread的方法start()同是開啟兩個線程,值得注意的是wait()函數,不能等待自己,這個是用來多個線程交互的,所以不能當sleep()用。這個函數是在主線程中被調用的時候阻塞了主線程。如果想在外部讓子線程暫停,最好的辦法是在子線程中設置一個標志,在主線程中更改這個標志,並在子線程的run函數中判斷,通過調用其保護函數sleep()來達到暫停的目的了。

查看源代碼,即可有清楚的概念:

 

[cpp]  view plain  copy
 
  1. bool QThread::wait(unsigned long time)  
  2. {  
  3.     Q_D(QThread);  
  4.     QMutexLocker locker(&d->mutex);   
  5.     if (d->id == GetCurrentThreadId()) {  
  6.         qWarning("QThread::wait: Thread tried to wait on itself");     //當是自身時,直接返回false  
  7.         return false;  
  8.     }  
  9.     if (d->finished || !d->running) //與這個線程對象關聯的線程已經結束執行(例如從run函數返回)。如果線程結束返回真值。如果線程還沒有開始也返回真值。  
  10.         return true;  
  11.     ++d->waiters;  
  12.     locker.mutex()->unlock();  
  13.   
  14.   
  15.     bool ret = false;  
  16.     switch (WaitForSingleObject(d->handle, time)) {   //調用win的對象處理函數  
  17.     case WAIT_OBJECT_0:    //核心對象被激活,等待成功  
  18.         ret = true;  
  19.         break;  
  20.     case WAIT_FAILED:  
  21.         qErrnoWarning("QThread::wait: Thread wait failure");       
  22.         break;  
  23.     case WAIT_ABANDONED:  
  24.     case WAIT_TIMEOUT:  
  25.     default:  
  26.         break;  
  27.     }  
  28.     locker.mutex()->lock();  
  29.     --d->waiters;  
  30.     if (ret && !d->finished) {                                  //雖然響應成功,但關聯對象未結束執行  
  31.         // thread was terminated by someone else  
  32.         d->terminated = true;              
  33.         QThreadPrivate::finish(this, false);  
  34.     }  
  35.     if (d->finished && !d->waiters) {    //關聯對象執行結束,並且等待數為零時,關閉句柄。  
  36.         CloseHandle(d->handle);  
  37.         d->handle = 0;  
  38.     }  
  39.     return ret;  
  40. }  



 

情形三:(Mutex 作用)

 

[cpp]  view plain  copy
 
  1. #include <QtCore/QCoreApplication>  
  2. #include <Qthread>  
  3. #include <QTextStream>  
  4. #include <QMutex>  
  5. class MyThreadA : public QThread {    
  6. public:      
  7. virtual void run();    
  8. };    
  9. class MyThreadB: public QThread {    
  10. public:      
  11. virtual void run();    
  12. };  
  13. QMutex mutex;  
  14. int number=6;  
  15. void MyThreadA::run(){    
  16. mutex.lock();  
  17. number *= 5;  
  18. sleep(1);  
  19. number /= 4;  
  20. mutex.unlock();  
  21. }    
  22. void MyThreadB::run(){  
  23. mutex.lock();  
  24. number *= 3;  
  25. sleep(1);  
  26. number /= 2;  
  27. mutex.unlock();  
  28. }   
  29. int main(int argc, char *argv[])  
  30. {     
  31. QCoreApplication app(argc, argv);  
  32. MyThreadA a;    
  33. MyThreadB b;    
  34. a.start();  
  35. b.start();    
  36. a.wait();    
  37. b.wait();    
  38. QTextStream out(stdout);  
  39. out<<number;  
  40. return app.exec();  
  41. }   


運行結果:

 

number=10;

通過實驗結果可以看出,QMutex保護了全局變量,同一時間只有一個線程可以訪問它。

只得一提的是tryLock()的使用,若以上代碼換為mutex.tryLock();那么執行結果可能為11,因為是試圖鎖定互斥量。如果鎖被得到,這個函數返回真。如果另一個進程已經鎖定了這個互斥量,這個函數返回假,而不是一直等到這個鎖可用為止。

且不能添上sleep()函數,否則提示 "A mutex must be unlocked in the same thread that locked it."的運行錯誤。

http://blog.csdn.net/mznewfacer/article/details/6966752


免責聲明!

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



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