QtQWaitCondition的正确使⽤⽅法
简单⽤法
QWaitCondition ⽤于多线程的同步,⼀个线程调⽤QWaitCondition::wait() 阻塞等待,直到另⼀个线程调⽤QWaitCondition::wake() 唤醒才继续往下执⾏。
为了描述⽅便,这⾥假设主线程调⽤Send()往通信⼝发送⼀个数据包,然后阻塞等待回包才继续往下执⾏。另⼀个线程(通信线程)不断从通信⼝中接收数据并解析成数据包,然后唤醒主线程。下⾯是按⽹上给的最简单的⽅法:
1// ⽰例⼀
2
3// 主线程
procedure什么意思4 Send(&packet);
udto的用法5 mutex.lock();
6 condition.wait(&mutex);
7if (m_receivedPacket)
8 {
9 HandlePacket(m_receivedPacket); // 另⼀线程传来回包green campus
10 }
11 mutex.unlock();
ballad12
全国大学生英语竞赛c类
13
14// 通信线程
15 m_receivedPacket = ParPacket(buffer); // 将接收的数据解析成包
timer
16 condition.wakeAll();
anchor通常情况下,上述代码能跑得很好。但在某些特殊情况下,可能会出现混乱,⼤⼤降低通信可靠性。
在主线程中,调⽤ Send(&packet) 发送后,假如通信线程⽴即收到回包,在主线程还来不及调⽤ wait() 的时候,已经先wakeAll() 了,显然这次唤醒是⽆效的,但主线程继续调⽤ wait(),然后⼀直阻塞在那⾥,因为该回的包已经回了。经测试出现这种现象的概率还是挺⼤的,因为我们不敢保证主线程总会被优先调度。即使主线程已经调⽤了 wait(),也不能保证底层操作系统的 wait_block 系统调⽤先于 wake 系统调⽤,毕竟wait() 函数也是层层封装的。
严谨⽤法
get fresh
QWaitCondition::wait() 在使⽤时必须传⼊⼀个上锁的 QMutex 对象。这是很有必要的。⽽上述⽰例⼀代码中,我们虽然⽤了mutex,但只是为了形式上传⼊QMutex参数,让编译器能正常编译⽽已,事实上,没有其它任何线程再⽤到这个mutex。⽽mutex 本来就是让多个线程能协调⼯作的,所以上述⽰例⼀主线程⽤的 mutex 是⽆效的。
根据 Qt ⼿册,wait() 函数必须传⼊⼀个已上锁的 mutex 对象,在 wait() 执⾏过程中,mutex⼀直保持上锁状态,直到调⽤操作系统的wait_block 在阻塞的⼀瞬间把 mutex 解锁(严格说来应该是原⼦操作,即系统能保证在真正执⾏阻塞等待指令时才解锁)。另⼀线程唤醒后,wait() 函数将在第⼀时间重新给 mutex 上锁(这种操作也是原⼦的),直到显⽰调⽤
mutex.unlock() 解锁。
在通信线程也⽤上 mutex 后,整个通信时序正常了,完全解决了⽰例⼀的问题。代码如下:
1// ⽰例⼆
2
3// 主线程
4 mutex.lock();
5 Send(&packet);
6 condition.wait(&mutex);
7if (m_receivedPacket)
8 {
9 HandlePacket(m_receivedPacket); // 另⼀线程传来回包六级听力高频词汇
10 }
11 mutex.unlock();
12
13
14// 通信线程
15 m_receivedPacket = ParPacket(buffer); // 将接收的数据解析成包奥巴马白宫记者晚宴
16 mutex.lock();
17 condition.wakeAll();
18 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 也都适⽤。