并发编程作为 C++11 系列的一个重大更新部分,值得我们去探究,并应用其提升程序的性能。本系列参考了其他一些文章,对 C++11 并发编程的一些要点进行了总结,并给出一些示例。
condition_variable 类介绍
std::condition_variable
是 C++11 多线程编程中的条件变量。
当 std::condition_variable
对象的某个 wait
类函数被调用的时候,它使用 std::unique_lock
(通过 std::mutex
)来锁住当前的线程,当前的线程会一直被阻塞(进入睡眠等待状态),直到有**其他的线程在同一个 std::condition_variable
对象上调用 notify
类函数来唤醒它。
std::condition_variable
对象通常使用 std::unique_lock<std::mutex>
来等待,如果需要使用另外的 lockable 类型,可以使用 std::condition_variable_any
类,本文后面会讲到 std::condition_variable_any
的用法。
首先来看一个简单的例子:
// condition_variable::notify_one #include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock #include <condition_variable> // std::condition_variable std::mutex mtx; std::condition_variable cv; bool ready = false; // 全局标志位 void printId(int id) { std::unique_lock<std::mutex> lck(mtx); // 如果标志位不为true,则等待 while(!ready) { // 线程被阻塞,直到标志位变为true cv.wait(lck); } std::cout << "thread: " << std::this_thread::get_id() << " id: " << id << " "; } void go() { std::unique_lock<std::mutex> lck(mtx); // 改变全局标志位 ready = true; // 唤醒所有线程 cv.notify_all(); } int main() { std::thread threads[10]; for (int i = 0; i < 10; ++i) { threads[i] = std::thread(printId, i); } std::cout << "create done. " ; go(); for (auto &t : threads) { t.join(); } std::cout << "process done. " ; return 0; }
root@ubuntu:~/c++# ./cond2 create done. thread: 281473284649424 id: 8 thread: 281473343398352 id: 1 thread: 281473335005648 id: 2 thread: 281473326612944 id: 3 thread: 281473318220240 id: 4 thread: 281473309827536 id: 5 thread: 281473301434832 id: 6 thread: 281473293042128 id: 7 thread: 281473276256720 id: 9 thread: 281473351791056 id: 0 process done.
在上面的例子中,10 个线程被同时唤醒,因此打印的时候是乱序的。值得注意的是 while(!ready)
,实际上,正常情况下,cv.wait
只会被调用一次,然后等待唤醒,因为线程在调用 wait()
之后就被阻塞了。但是通过一个 while
循环来判断全局标志位是否正确,这样可以防止被误唤醒,这也是条件变量中的常见写法。
https://murphypei.github.io/blog/2019/04/cpp-concurrent-3.html