(C++) 笔记 C++11 std::mutex std::condition_variable 的使用

2023-02-12,,,

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread> int main() {
constexpr size_t kLoopNum = 10;
std::mutex mtx;
std::condition_variable cv;
bool ready_flag{false}; std::thread thd_producer([&]() {
for (size_t i = 0; i < kLoopNum; i++) {
std::cout << "producer thread " << i << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (!ready_flag) {
std::unique_lock<std::mutex> lock;
ready_flag = true;
cv.notify_one();
}
}
}); std::thread thd_consumer([&]() {
for (size_t i = 0; i < kLoopNum; i++) {
std::cout << "consumer thread " << i << std::endl;
std::unique_lock<std::mutex> lock;
cv.wait(lock, [&]() {
return !ready_flag;
});
}
}); thd_producer.join();
thd_consumer.join();
}

输出信息:

producer thread 0
consumer thread 0
consumer thread 1
consumer thread 2
consumer thread 3
consumer thread 4
consumer thread 5
consumer thread 6
consumer thread 7
consumer thread 8
consumer thread 9
producer thread 1
producer thread 2
producer thread 3
producer thread 4
producer thread 5
producer thread 6
producer thread 7
producer thread 8
producer thread 9

使用解读

如下 a, b 用法等效

std::unique_lock <std::mutex> lck(mtx);
// cv.wait(lck, [&](){return ready;}); // (a)
while(!ready) cv.wait(lck); // (b)

条件变量ready必须位于lock中,以保证内存序

cpp reference: conditional_variable

Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread.

Any thread that intends to wait on std::condition_variable has to

    acquire a std::unique_lockstd::mutex, on the same mutex as used to protect the shared variable
    either A

    2.1 check the condition, in case it was already updated and notified

    2.2 execute wait, wait_for, or wait_until. The wait operations atomically release the mutex and suspend the execution of the thread.

    2.3 When the condition variable is notified, a timeout expires, or a spurious wakeup occurs, the thread is awakened, and the mutex is atomically reacquired. The thread should then check the condition and resume waiting if the wake up was spurious.

    or B

    use the predicated overload of wait, wait_for, and wait_until, which takes care of the three steps above

C++11中的lock

std::unique_lock
std::lock_guard
std::scoped_lock
std::lock

C++11中的lock都属于资源自动管理(RAII)范畴。

unique_lock 在使用上比lock_guard更具有弹性,和 lock_guard 相比,unique_lock 主要的特色在于:

    unique_lock 不一定要拥有 mutex,所以可以透过 default constructor 建立出一个空的 unique_lock。
    unique_lock 虽然一样不可复制(non-copyable),但是它是可以转移的(movable)。所以,unique_lock 不但可以被函数回传,也可以放到 STL 的 container 里。
    另外,unique_lock 也有提供 lock()、unlock() 等函数,可以用来加锁解锁mutex,也算是功能比较完整的地方。
    unique_lock本身还可以用于std::lock参数,因为其具备lock、unlock、try_lock成员函数,这些函数不仅完成针对mutex的操作还要更新mutex的状态。

conditional_variable的notidy

    cv.notify_one
    cv.notify_all
    std::notify_all_at_thread_exit(...)

参考

C++11 并发指南五(std::condition_variable 详解)
C++11 std::unique_lock与std::lock_guard区别及多线程应用实例
cpp reference -- std::unique_lock

(C++) 笔记 C++11 std::mutex std::condition_variable 的使用的相关教程结束。

《(C++) 笔记 C++11 std::mutex std::condition_variable 的使用.doc》

下载本文的Word格式文档,以方便收藏与打印。