使用条件变量(condition_variable)进行线程间同步

BusyWaitableCondition是忙寻版的可等待条件

WaitableCondition是睡眠版的可等待条件

两者实现相同功能,只是忙寻版会占用一个cpu core,而睡眠版会陷入睡眠在需要时才被唤醒继续执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

#include <iostream>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <thread>
#include <future>

using namespace std;

class BusyWaitableCondition {
public:
void Wait() {
while(!_flag);
}

void Notify() {
_flag = true;
}

void Reset() {
_flag = false;
}

private:
std::atomic<bool> _flag = false;
};

class WaitableCondition {
public:
void Wait() {
std::unique_lock<std::mutex> lock(_mutex);
if(!_flag) {
_cv.wait(lock, [this](){ return _flag; });
}
}

void Notify() {
do {
std::lock_guard<std::mutex> lock(_mutex);
_flag = true;
} while(false);
_cv.notify_all();
}

void Reset() {
std::lock_guard<std::mutex> lock(_mutex);
_flag = false;
}

private:
std::mutex _mutex;
std::condition_variable _cv;
bool _flag = false;
};

int main() {
WaitableCondition c1;
auto fut1 = std::async(std::launch::async, [&]{
c1.Wait(); // 等待条件1满足才继续执行
cout << "Hello 1" << endl;
});
auto fut2 = std::async(std::launch::async, [&]{
c1.Wait(); // 等待条件1满足才继续执行
cout << "Hello 2" << endl;
});

this_thread::sleep_for(chrono::seconds(1));
cout << "1" << endl;
cout << "2" << endl;
cout << "3" << endl;
cout << "4" << endl;
cout << "5" << endl;
c1.Notify(); // 通知条件1满足了

return 0;
}

上面的例子中有定义Reset方法,但是没有演示,它的作用是将条件设置为否,以便可以再次等待,然后可以重新触发。

如果只需要等待一个一次性事件而不需要Reset方法那么可以用std::promise+std::future进行更简单的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class WaitableEvent {
public:
WaitableEvent(): _fut(_promise.get_future()) {

}

void Wait() const {
_fut.wait();
}

void Notify() {
_promise.set_value();
}

private:
std::promise<void> _promise;
std::future<void> _fut;
};