mutex

mutex 是可以被 lock 的对象,用来保护临界区的
我程A.png

  1. std::mutex mtx;
  2. mtx.lock();
  3. // 临界区
  4. mtx.unlock();

mutextry_lock 方法:加锁失败,不会阻塞

lock

lock 是用来管理 mutex 对象的,在 lock 的「构造函数」中对 mutex进行加锁,在「析构函数」中解锁。

常用的 lock有:lock_guardunique_lock

**lock_guard**
管理 mutex 对象,构造函数进行加锁,析构函数解锁
这样做的好处是,即使发生了异常也不会出问题

  1. #include <mutex>
  2. std::mutex mtx;
  3. void run() {
  4. try {
  5. std::lock_guard<std::mutex> lck(mtx);
  6. // 临界区
  7. } catch (...) {}
  8. }

**unique_lock**
unique_lock 也是类似的,只不过比 lock_guard 灵活

条件变量

条件变量提供了通知和唤醒的机制:

  • 调用 wait 使线程自身陷入阻塞态,直到被其他线程唤醒;wait_for 类似,可以设置最长等待时间
  • 调用notify_one 唤醒等待的一个线程;notify_all 唤醒等待的所有线程

我程A.png

条件变量需要和锁一起使用:

  • 条件变量只提供了等待队列和通知机制,并没有提供互斥机制,所以需要和锁一起使用。考虑多个线程同时调用条件变量的 wait 方法,由于没有锁的互斥保证,一些线程可能无法正确地挂载到等待队列上。
  • unique_lock 使用来保护条件变量的

wait(lock)的原理

调用 wait() 的线程,其代码如下:

  1. std::unique_lock<std::mutex> lck(mtx);
  2. cdt.wait(lck);

在调用 wait 时,实际相当于执行了如下语句(伪代码):

  1. // 1. 将当前线程挂载到条件变量的等待队列
  2. cdt.add_to_queue(this_thread);
  3. // 2. 释放锁
  4. lck.unlock();
  5. // 3. 进入阻塞等待
  6. this_thread.wait();
  7. // -------------------
  8. // 4. 被其他线程唤醒
  9. lck.lock();

使用的例子

  1. std::mutex mtx;
  2. std::condition_variable cond;
  3. void thread_A() {
  4. std::unique_lock<std::mutex> lock(mtx);
  5. std::cout << "wait" << std::endl;
  6. cond.wait(lock);
  7. std::cout << "awake" << std::endl;
  8. }
  9. void thread_B() {
  10. cond.notify_one();
  11. }
  12. int main() {
  13. std::thread tA(thread_A, nullptr);
  14. std::thread_tB(thread_B, nullptr);
  15. tA.join();
  16. tB.join();
  17. }