C++标准库中的底层同步原语。理解std::condition_variable,着重就在于理解std::condition_variable::wait()这个函数,签名为void(std::unique_lock<std::mutex>& lk, Predicate pred),Predicate代表谓词。
wait 的作用
一句话解释:阻塞当前线程,直至被其他线程通过notify_all()或者notify_one()唤醒,并且满足谓词条件。notify*()也是std::condition_variable的成员函数。
如何调用
线程在调用`wait()`之前必须先获得锁,也就是说,必须是如下所示配套的调用:
1 | std::condition_variable cv; |
内部流程
如图 1 所示。加锁之后,线程 A 独占资源,然后判断谓词是否满足。
- 满足谓词:OK,跳出 wait,执行程序的其他部分;
- 不满足谓词:将锁解开,并且线程 A 阻塞,等待其他线程将其唤醒;
虚假唤醒
spurious wakeup。为避免这种情况,系统在实现wait的时候使用while循环,如下所示为等价形式:
1 | while(!pred()){ |
应用场景
直接拿 leetcode 多线程中的一道题目来阐述。
我们提供一个类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 class FooBar {
public void foo() {
for (int i = 0; i < n; i++) {
print("foo");
}
}
public void bar() {
for (int i = 0; i < n; i++) {
print("bar");
}
}
}
两个不同的线程将会共用一个 FooBar 实例。
其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。
请设计修改程序,以确保 "foobar" 被输出 n 次。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/print-foobar-alternately
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
简单来说,就是希望线程 A 和线程 B 能够交替进行 print。显然,这涉及到了进程间的通信。
运用condition_variable进行线程通信的原理就是:设定一个变量,不同的线程需要针对该变量满足不同的条件时,才能够继续执行,否则阻塞;执行完毕后必须更改变量,并且notify别的线程。
了解了原理,那就好办了,我们就设一个变量int index;,当其为偶数时,执行线程 A,否则执行线程 B。需要注意的是:使用 RAII 模式。
1 |
|
替代信号量
“Semaphore has no notion of ownership.” semaphore 作为底层原语,其并不完备,也不是必备的。semaphore 的功能完全可以通过 condition_variable+mutex 来替代。