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(thisfalse);  
  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."的运行错误。

你可能感兴趣的:(Qt多线程编程总结(二)——QMutex)