QWaitCondition 的正确使用方法

简单用法
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();

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

在主线程中,调用 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();

上述示例二中,主线程先把 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 也都适用。
————————————————
版权声明:本文为CSDN博主「flyoxs」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/flyoxs/article/details/54617342

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QWaitCondition是一个用于多线程同步的类,它允许一个线程在满足特定条件之前等待,直到另一个线程唤醒它。\[1\]在Qt中,可以使用QWaitConditionwait()函数来阻塞一个线程,直到另一个线程调用wake()函数来唤醒它。\[1\]在给定的例子中,myThread1线程通过循环增加变量i的值,当i达到一定条件时,它调用waitCondition的wakeOne()函数来唤醒myThread2线程。\[3\]而myThread2线程在开始时先加锁,然后检查i的值是否满足特定条件,如果不满足,则调用waitConditionwait()函数来等待唤醒。\[3\]当myThread1线程唤醒myThread2线程后,myThread2线程解锁并继续执行。\[3\]这样,通过QWaitCondition使用,可以实现多线程之间的同步。 #### 引用[.reference_title] - *1* [QWaitCondition正确使用方法qt线程同步)](https://blog.csdn.net/f110300641/article/details/108822920)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Qt线程:QWaitCondition](https://blog.csdn.net/kenfan1647/article/details/118636579)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值