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. void someMethod()
  3. {
  4. mutex.lock();
  5. qDebug("Hello");
  6. qDebug("World");
  7. mutex.unlock();
  8. }

用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. bool ret = false;
  14. switch (WaitForSingleObject(d->handle, time)) {   //调用win的对象处理函数
  15. case WAIT_OBJECT_0:    //核心对象被激活,等待成功
  16. ret = true;
  17. break;
  18. case WAIT_FAILED:
  19. qErrnoWarning("QThread::wait: Thread wait failure");
  20. break;
  21. case WAIT_ABANDONED:
  22. case WAIT_TIMEOUT:
  23. default:
  24. break;
  25. }
  26. locker.mutex()->lock();
  27. --d->waiters;
  28. if (ret && !d->finished) {                                  //虽然响应成功,但关联对象未结束执行
  29. // thread was terminated by someone else
  30. d->terminated = true;
  31. QThreadPrivate::finish(this, false);
  32. }
  33. if (d->finished && !d->waiters) {    //关联对象执行结束,并且等待数为零时,关闭句柄。
  34. CloseHandle(d->handle);
  35. d->handle = 0;
  36. }
  37. return ret;
  38. }

情形三:(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

时间: 2024-10-17 08:19:39

Qt多线程编程总结(二)——QMutex的相关文章

QT开发(三十四)——QT多线程编程

QT开发(三十四)--QT多线程编程 一.QT多线程简介 QT通过三种形式提供了对线程的支持,分别是平台无关的线程类.线程安全的事件投递.跨线程的信号-槽连接. QT中线程类包含如下: QThread 提供了开始一个新线程的方法    QThreadStorage 提供逐线程数据存储    QMutex 提供相互排斥的锁,或互斥量    QMutexLocker 是一个辅助类,自动对 QMutex 加锁与解锁    QReadWriterLock 提供了一个可以同时读操作的锁    QReadL

Qt多线程编程总结(一)(所有GUI对象都是线程不安全的)

Qt对线程提供了支持,基本形式有独立于平台的线程类.线程安全方式的事件传递和一个全局Qt库互斥量允许你可以从不同的线程调用Qt方法. 这个文档是提供给那些对多线程编程有丰富的知识和经验的听众的.推荐阅读: Threads Primer: A Guide to Multithreaded Programming Thread Time: The Multithreaded Programming Guide Pthreads Programming: A POSIX Standard for Be

QT多线程编程经验

自动化生产设备软件开发背景,主要关于视觉引导.运动控制和人机交互.三年的开发积累,基本都是在QT+VS2013平台上进行.本编随笔主要总结下多线程编程上的经验. 背景 上图是一个用于机器视觉引导的软件截图,软件由七个工作线程组成,各个工作线程负责接收控制系统的引导请求,采集图像后进行处理分析.模式匹配和坐标转换后,返回目标坐标数据.工作中使用的通信方式的TCP/IP以太网通信,在后文会提及到两种通信方式,一是工作线程内的同步方式,二是代理线程内异步方式.

多线程编程(二)NSThread的使用

iOS 支持多个层次的多线程编程,层次越高的抽象程度越高,使用也越方便.实现多线程的方式有很多,我们主要学习其中的三种实现方式:NSThread,NSOpreationQueue,GCD,这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,GCD是抽象程度最高的一种,也是 Apple 最推荐使用的方法. 虽然实现的方式不同,但是操作的对象都是线程,都是对线程的管理.所以,从根本上讲,三者并没有什么不同,可以混合使用,但是并不推荐,因为可能会造成不必要的麻烦.三种多线程的实现方

Java多线程编程(二)

在 Java多线程编程(一) 中的多线程并没有返回值,本文将介绍带返回值的多线程. 要想有返回值,则需要实现新的接口Callable而不再是Runnable接口,实现的方法也改为call()方法,执行器也不再是调用execute(),而是submit() [程序实例] 1 public class TaskWithResult implements Callable<String> { 2 3 private int id; 4 5 public TaskWithResult(int id)

Java多线程编程(二)对象及变量的并发访问

一.synchronized同步方法 1.方法内的变量为线程安全 2.实例变量非线程安全 3.多个对象多个锁 4.synchronized方法与锁对象 5.脏读 6.synchronized锁冲入 7.出现异常,锁自动释放 8.同步不具有继承性 二.synchronized同步语句块 1.synchronized方法的弊端 2.synchronized同步代码块的使用 3.用同步代码块解决同步的弊端 4.一半异步,一半同步 5.synchronized代码块间的同步性 6.验证同步synchro

Java多线程编程(二)操作状态与常用方法

线程的状态: 1.线程也有固定的操作状态: 创建状态:准备好了一个多线程的对象(也就是new出来一个Thread对象) 就绪状态:调用了start()方法,等待CPU进行调度(等待CPU分配资源的状态) 运行状态:执行run()方法(当CPU分配好资源或CPU已经进行调度,此时处于运行状态) 阻塞状态:暂时停止执行,可能将资源交给其他线程使用(可再恢复到运行状态) 终止状态(死亡状态):线程销毁(最终当整个线程执行完毕之后,线程自动销毁) 线程的常用方法: 线程的常用方法大都存在于Thread类

APUE学习之多线程编程(二):线程同步

为了保证临界资源的安全性和可靠性,线程不得不使用锁,同一时间只允许一个或几个线程访问变量.常用的锁有互斥量,读写锁,条件变量 一.互斥量 互斥量是用pthread_mutex_t数据类型表示的,在使用之前,必须对其进行初始化,可以把它设置为PTHREAD_MUTEX_INITIALIZER(只适于静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化,最后还要调用pthread_mutex_destroy进行释放. #include <pthread.h> int

java多线程同步以及线程间通信详解&amp;消费者生产者模式&amp;死锁&amp;Thread.join()(多线程编程之二)

本篇我们将讨论以下知识点: 1.线程同步问题的产生 什么是线程同步问题,我们先来看一段卖票系统的代码,然后再分析这个问题: [java] view plain copy print? package com.zejian.test; /** * @author zejian * @time 2016年3月12日 下午2:55:42 * @decrition 模拟卖票线程 */ public class Ticket implements Runnable { //当前拥有的票数 private