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

#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

  1. acquire a std::unique_lockstd::mutex, on the same mutex as used to protect the shared variable
  2. 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 主要的特色在于:

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

conditional_variable的notidy

  1. cv.notify_one
  2. cv.notify_all
  3. std::notify_all_at_thread_exit(...)

参考

posted @ 2022-10-20 20:58  山岚2013  阅读(109)  评论(0编辑  收藏  举报