Qt多线程编程总结(二)——QMutex .

QMutex类提供的是线程之间的访问顺序化。

QMutex的目的是保护一个对象、数据结构或者代码段,所以同一时间只有一个线程可以访问它。(在Java术语中,它和同步关键字“synchronized”很相似)。例如,这里有一个方法打印给用户两条消息:

  1. void someMethod()  
  2. {  
  3.    qDebug("Hello");  
  4.    qDebug("World");  
  5. }  
  void someMethod()
  {
     qDebug("Hello");
     qDebug("World");
  }
  

如果同时在两个线程中调用这个方法,结果的顺序将是:

  Hello
  Hello
  World
  World
  

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

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

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

用Java的术语,这段代码应该是:

  1. void someMethod()  
  2. {  
  3.    synchronized {  
  4.      qDebug("Hello");  
  5.      qDebug("World");  
  6.    }  
  7. }  
  void someMethod()
  {
     synchronized {
       qDebug("Hello");
       qDebug("World");
     }
  }
  

然后同一时间只有一个线程可以运行someMethod并且消息的顺序也一直是正确的。当然,这只是一个很简单的例子,但是它适用于任何需要按特定频率发生的情况。

但你在一个线程中调用lock(),其它线程将会在同一地点试图调用lock()来阻塞,知道这个线程调用unlock()之后其它线程才会获得这个锁。lock()的一种非阻塞选择是tryLock()。

实验部分:

情形一:

  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. }   
#include <QtCore/QCoreApplication>
#include <Qthread>
#include <QTextStream>
class MyThreadA : public QThread {  
public:    
virtual void run();  
};  
class MyThreadB: public QThread {  
public:    
virtual void run();  
};
int number=6;
void MyThreadA::run(){  
number *= 5;
number /= 4;
}  
void MyThreadB::run(){
number *= 3;
number /= 2;
} 
int main(int argc, char *argv[])
{   
QCoreApplication app(argc, argv);
MyThreadA a;  
MyThreadB b;  
a.run();
b.run();
 a.terminate();
b.terminate();
QTextStream out(stdout);
out<<number;
return app.exec();
} 

上述代码,很简单,写了两个线程,覆盖了QThread的纯虚函数run(),这两个重构的run方法都是对全局变量number的操作,

主函数中顺序调用这两个方法,a.run()执行后number为7,b.run()执行后为10。

情形二:

  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. }   
#include <QtCore/QCoreApplication>
#include <Qthread>
#include <QTextStream>
class MyThreadA : public QThread {  
public:    
virtual void run();  
};  
class MyThreadB: public QThread {  
public:    
virtual void run();  
};
int number=6;
void MyThreadA::run(){  
number *= 5;
sleep(1);
number /= 4;
}  
void MyThreadB::run(){
number *= 3;
sleep(1);
number /= 2;
} 
int main(int argc, char *argv[])
{   
QCoreApplication app(argc, argv);
MyThreadA a;  
MyThreadB b;  
a.start();
b.start();  
a.wait();  
b.wait(); 
QTextStream out(stdout);
out<<number;
return app.exec();
} 

运行结果:

number=11;

利用QThread的方法start()同是开启两个线程,值得注意的是wait()函数,不能等待自己,这个是用来多个线程交互的,所以不能当sleep()用。这个函数是在主线程中被调用的时候阻塞了主线程。如果想在外部让子线程暂停,最好的办法是在子线程中设置一个标志,在主线程中更改这个标志,并在子线程的run函数中判断,通过调用其保护函数sleep()来达到暂停的目的了。

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

  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. }  
bool QThread::wait(unsigned long time)
{
    Q_D(QThread);
    QMutexLocker locker(&d->mutex); 
    if (d->id == GetCurrentThreadId()) {
        qWarning("QThread::wait: Thread tried to wait on itself");     //当是自身时,直接返回false
        return false;
    }
    if (d->finished || !d->running) //与这个线程对象关联的线程已经结束执行(例如从run函数返回)。如果线程结束返回真值。如果线程还没有开始也返回真值。
        return true;
    ++d->waiters;
    locker.mutex()->unlock();


    bool ret = false;
    switch (WaitForSingleObject(d->handle, time)) {   //调用win的对象处理函数
    case WAIT_OBJECT_0:    //核心对象被激活,等待成功
        ret = true;
        break;
    case WAIT_FAILED:
        qErrnoWarning("QThread::wait: Thread wait failure");     
        break;
    case WAIT_ABANDONED:
    case WAIT_TIMEOUT:
    default:
        break;
    }
    locker.mutex()->lock();
    --d->waiters;
    if (ret && !d->finished) {                                  //虽然响应成功,但关联对象未结束执行
        // thread was terminated by someone else
        d->terminated = true;            
        QThreadPrivate::finish(this, false);
    }
    if (d->finished && !d->waiters) {    //关联对象执行结束,并且等待数为零时,关闭句柄。
        CloseHandle(d->handle);
        d->handle = 0;
    }
    return ret;
}


情形三:(Mutex 作用)

  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. }   
#include <QtCore/QCoreApplication>
#include <Qthread>
#include <QTextStream>
#include <QMutex>
class MyThreadA : public QThread {  
public:    
virtual void run();  
};  
class MyThreadB: public QThread {  
public:    
virtual void run();  
};
QMutex mutex;
int number=6;
void MyThreadA::run(){  
mutex.lock();
number *= 5;
sleep(1);
number /= 4;
mutex.unlock();
}  
void MyThreadB::run(){
mutex.lock();
number *= 3;
sleep(1);
number /= 2;
mutex.unlock();
} 
int main(int argc, char *argv[])
{   
QCoreApplication app(argc, argv);
MyThreadA a;  
MyThreadB b;  
a.start();
b.start();  
a.wait();  
b.wait();  
QTextStream out(stdout);
out<<number;
return app.exec();
} 

运行结果:

number=10;

通过实验结果可以看出,QMutex保护了全局变量,同一时间只有一个线程可以访问它。

只得一提的是tryLock()的使用,若以上代码换为mutex.tryLock();那么执行结果可能为11,因为是试图锁定互斥量。如果锁被得到,这个函数返回真。如果另一个进程已经锁定了这个互斥量,这个函数返回假,而不是一直等到这个锁可用为止。

且不能添上sleep()函数,否则提示 "A mutex must be unlocked in the same thread that locked it."的运行错误。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值