简单用法

QWaitCondition 用于多线程的同步,一个线程调用QWaitCondition::wait() 阻塞等待,直到另一个线程调用QWaitCondition::wake() 唤醒才继续往下执行。

为了描述方便,这里假设主线程调用Send()往通信口发送一个数据包,然后阻塞等待回包才继续往下执行。另一个线程(通信线程)不断从通信口中接收数据并解析成数据包,然后唤醒主线程。下面是按网上给的最简单的方法:

// 示例一

// 主线程
Send(&packet);
mutex.lock();
condition.wait(&mutex);
if (m_receivedPacket)
{
HandlePacket(m_receivedPacket); // 另一线程传来回包
}
mutex.unlock(); // 通信线程
m_receivedPacket = ParsePacket(buffer); // 将接收的数据解析成包
condition.wakeAll();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

通常情况下,上述代码能跑得很好。但在某些特殊情况下,可能会出现混乱,大大降低通信可靠性。

在主线程中,调用 Send(&packet) 发送后,假如通信线程立即收到回包,在主线程还来不及调用 wait() 的时候,已经先 wakeAll() 了,显然这次唤醒是无效的,但主线程继续调用 wait(),然后一直阻塞在那里,因为该回的包已经回了。经测试出现这种现象的概率还是挺大的,因为我们不敢保证主线程总会被优先调度。即使主线程已经调用了 wait(),也不能保证底层操作系统的 wait_block 系统调用先于 wake 系统调用,毕竟wait() 函数也是层层封装的。

严谨用法

QWaitCondition::wait() 在使用时必须传入一个上锁的 QMutex 对象。这是很有必要的。而上述示例一代码中,我们虽然用了 mutex,但只是为了形式上传入QMutex参数,让编译器能正常编译而已,事实上,没有其它任何线程再用到这个mutex。而 mutex 本来就是让多个线程能协调工作的,所以上述示例一主线程用的 mutex 是无效的。

根据 Qt 手册,wait() 函数必须传入一个已上锁的 mutex 对象,在 wait() 执行过程中,mutex一直保持上锁状态,直到调用操作系统的wait_block 在阻塞的一瞬间把 mutex 解锁(严格说来应该是原子操作,即系统能保证在真正执行阻塞等待指令时才解锁)。另一线程唤醒后,wait() 函数将在第一时间重新给 mutex 上锁(这种操作也是原子的),直到显示调用 mutex.unlock() 解锁。

在通信线程也用上 mutex 后,整个通信时序正常了,完全解决了示例一的问题。代码如下:

// 示例二

// 主线程
mutex.lock();
Send(&packet);
condition.wait(&mutex);
if (m_receivedPacket)
{
HandlePacket(m_receivedPacket); // 另一线程传来回包
}
mutex.unlock(); // 通信线程
m_receivedPacket = ParsePacket(buffer); // 将接收的数据解析成包
mutex.lock();
condition.wakeAll();
mutex.unlock();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

上述示例二中,主线程先把 mutex 锁占据,即从发送数据包开始,一直到 QWaitCondition::wait() 在操作系统层次真正执行阻塞等待指令,这一段主线程的时间段内,mutex 一直被上锁,即使通信线程很快就接收到数据包,也不会直接调用 wakeAll(),而是在调用 mutex.lock() 时阻塞住(因为主线程已经把mutex占据上锁了,再尝试上锁就会被阻塞),直到主线程 QWaitCondition::wait() 真正执行操作系统的阻塞等待指令并释放mutex,通信线程的 mutex.lock() 才即出阻塞,继续往下执行,调用 wakeAll(),此时一定能唤醒主线程成功。

由此可见,通过 mutex 把有严格时序要求的代码保护起来,同时把 wakeAll() 也用同一个 mutex 保护起来,这样能保证:一定先有 wait() ,再有 wakeAll(),不管什么情况,都能保证这种先后关系,而不至于摆乌龙。

推而广之

mutex 和 condition 联合使用是多线程中的一个常用的设计模式,不仅是 Qt,对于 C++ 的 std::condition_variable 和 std::mutex ,以及 java 的 synchronized / wait / notify 也都适用。

https://blog.csdn.net/flyoxs/article/details/54617342

在线求助,,QWaitCondition wait 把主线程都给阻塞住了,不管是串口来数据还是什么,都不能动了~~ 怎么办?? 感谢感谢!!!

测试了下,如果没有其他条件限制,单靠lock上锁,则线程还是可能锁死,因为wakeAll所在线程也可能先得到锁,还是会产生导致线程先wakeAll再wait的情况

QWaitCondition 的正确使用方法(通过 mutex 把有严格时序要求的代码保护起来,同时把 wakeAll() 也用同一个 mutex 保护起来)的更多相关文章

  1. thinkphp3.2 cli模式的正确使用方法

    最近要使用thinkphp3.2版本的cli模式,手动执的话没有问题,比如php /www/index.php home/article/get 这样没有问题,但是一般用cli模式都是定时任务比较多, ...

  2. Linux重启inotify配置max_user_watches无效被恢复默认值8192的正确修改方法

    Linux下Rsync+inotify-tools实现数据实时同步中有一个重要的配置就是设置Inotify的max_user_watches值,如果不设置,当遇到大量文件的时候就会出现出错的情况. 一 ...

  3. MyEclipse10的正确破解方法

    无法转载,故给出原文链接,以供需要者. MyEclipse10的正确破解方法

  4. [转]MySQL忘记密码的正确解决方法

    http://database.51cto.com/art/201005/201986.htm 以下的文章主要介绍的是MySQL忘记密码的正确解决方法,在实际操作中如果你忘记MySQL密码是一件很头痛 ...

  5. webpack快速入门——实战技巧:watch的正确使用方法,webpack自动打包

    随着项目大了,后端与前端联调,我们不需要每一次都去打包,这样特别麻烦,我们希望的场景是,每次按保存键,webpack自动为我们打包,这个工具就是watch! 因为watch是webpack自带的插件, ...

  6. .Net core 下的ConfigurationManager类正确引用方法

    大家在项目中经常会用到需要引用配置文件的情况,这也是我偶然间遇到的问题,菜鸟一枚,如有需纠正多谢指点. 正题 在不先引用using的情况下直接写 ConfigurationManager.AppSet ...

  7. SpringBoot 中 @RequestBody的正确使用方法

    SpringBoot 中 @RequestBody的正确使用方法 最近在接收一个要离职同事的工作,接手的项目是用SpringBoot搭建的,其中看到了这样的写法: @RequestMapping(&q ...

  8. CentOS正确关机方法(转)

    CentOS正确关机方法 1关机前准备 1.1观察系统使用状态 ·         谁在线:who ·         联网状态:netstat -a ·         后台执行的程序:ps -au ...

  9. windows server 2008 R2 的 FTP 防火墙的正确配置方法

    存在问题 FTP搭建完成后,仅本机可以访问,其他机器无法访问. 解决方案 这时,将C:\Windows\System32\svchost.exe添加到例外即可正常访问,如下图所示.将20及21端口添加 ...

随机推荐

  1. docker -mysql服务设置远程连接 解决1251 client does not support ..问题

    前提: 安装MYSQL实例 docker pull mysql 启动mysql(做了端口映射) [root@localhost ~]# docker run -p 3306:3306 --name m ...

  2. rtsp和sdp协议简介

    RTSP是由Real network 和Netscape共同提出的如何有效地在IP网络上传输流媒体数据的应用层协议. 实时流协议(RTSP)建立并控制一个或几个时间同步的连续流媒体,如音频和视频.尽管 ...

  3. 57.C++处理转义字符

    #include <iostream> #include <string> #include <cstdlib> using namespace std; void ...

  4. Kinect 开发 —— 深度信息(二)

    转自(并致谢):http://www.cnblogs.com/yangecnu/archive/2012/04/05/KinectSDK_Depth_Image_Processing_Part2.ht ...

  5. HDU 4731 Minimum palindrome 打表找规律

    http://acm.hdu.edu.cn/showproblem.php?pid=4731 就做了两道...也就这题还能发博客了...虽然也是水题 先暴力DFS打表找规律...发现4个一组循环节.. ...

  6. Markdown最简单常用的语法

    1,文本强调: 文本倾斜,*我是倾斜的文本* 文本加粗,**我是加粗的文本** 文本删除线,~~带删除线的文本~~ 2,链接,分为行内式与参数式,参数式多用于在文章中多次使用相同的链接 行内式写法:[ ...

  7. 有关cascade的结构体

    /* internal cascade classifier */ typedef struct CvCascadeHaarClassifier { CV_INT_HAAR_CLASSIFIER_FI ...

  8. Day4晚笔记

    数据结构 并查集:捆绑两个点的信息,判断对错 倍增:LCA, 字符串 hash,模拟, 最小表示法 给定一个环状字符串,切开,使得字符串的字典序最小 图和树 割点,割边,强联通分量 点双联通分量 (把 ...

  9. golang beego cache

    package main import ( "fmt" "github.com/astaxie/beego/cache" "time" ) ...

  10. 玲珑杯 Round 19 B Buildings (RMQ + 二分)

    DESCRIPTION There are nn buildings lined up, and the height of the ii-th house is hihi. An inteval [ ...