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

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

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

  void someMethod()
  {
     qDebug("Hello");
     qDebug("World");
  }
  

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

  Hello
  Hello
  World
  World
  

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

  QMutex mutex;

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

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

  void someMethod()
  {
     synchronized {
       qDebug("Hello");
       qDebug("World");
     }
  }
  

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

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

实验部分:

情形一:

#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。

情形二:

#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()来达到暂停的目的了。

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

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 作用)

#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."的运行错误。



  • 17
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Qt对线程提供了支持,基本形式有独立于平台的线程类、线程安全方式的事件传递和一个全局Qt库互斥量允许你可以从不同的线程调用Qt方法。 这个文档是提供给那些对多线程编程有丰富的知识和经验的听众的。推荐阅读: Threads Primer: A Guide to Multithreaded Programming Thread Time: The Multithreaded Programming Guide Pthreads Programming: A POSIX Standard for Better Multiprocessing (O'Reilly Nutshell) Win32 Multithreaded Programming 警告:所有的GUI类(比如,QWidget和它的子类),操作系统核心类(比如,QProcess)和网络类都不是线程安全的。 QRegExp使用一个静态缓存并且也不是线程安全的,即使通过使用QMutex来保护的QRegExp对象。 启用线程支持 在Windows上安装Qt时,在一些编译器上线程支持是一个选项。 在Mac OS X和Unix上,线程支持可以当你在运行configure脚本时添加-thread选项就可以生效了。在Unix平台上,多线程程序必须用特殊的方式连接,比如使用特殊的libc,安装程序将会创建另外一个库libqt-mt并且因此线程程序必须和这个库进行连接(使用-lqt-mt)而不是标准的Qt库。 在两个平台上,你都应该定义宏QT_THREAD_SUPPORT来编译(比如,编译时使用-DQT_THREAD_SUPPORT)。在Windows上,这个通常可以在qconfig.h写一个条目来解决。 线程类 最重要的类是QThread,也就是说要开始一个新的线程,就是开始执行你重新实现的QThread::run()。这和Java的线程类很相似。 为了写线程程序,在两个线程同时希望访问同一个数据时,对数据进行保护是很必要的。因此这里也有一个QMutex类,一

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值