mutex
mutex 是可以被 lock 的对象,用来保护临界区的
std::mutex mtx;mtx.lock();// 临界区mtx.unlock();
mutex 有 try_lock 方法:加锁失败,不会阻塞
lock
lock 是用来管理 mutex 对象的,在 lock 的「构造函数」中对 mutex进行加锁,在「析构函数」中解锁。
常用的 lock有:lock_guard和 unique_lock。
**lock_guard**:
管理 mutex 对象,构造函数进行加锁,析构函数解锁
这样做的好处是,即使发生了异常也不会出问题
#include <mutex>std::mutex mtx;void run() {try {std::lock_guard<std::mutex> lck(mtx);// 临界区} catch (...) {}}
**unique_lock**:unique_lock 也是类似的,只不过比 lock_guard 灵活
条件变量
条件变量提供了通知和唤醒的机制:
- 调用
wait使线程自身陷入阻塞态,直到被其他线程唤醒;wait_for类似,可以设置最长等待时间 - 调用
notify_one唤醒等待的一个线程;notify_all唤醒等待的所有线程

条件变量需要和锁一起使用:
- 条件变量只提供了等待队列和通知机制,并没有提供互斥机制,所以需要和锁一起使用。考虑多个线程同时调用条件变量的
wait方法,由于没有锁的互斥保证,一些线程可能无法正确地挂载到等待队列上。 unique_lock使用来保护条件变量的
wait(lock)的原理
调用 wait() 的线程,其代码如下:
std::unique_lock<std::mutex> lck(mtx);cdt.wait(lck);
在调用 wait 时,实际相当于执行了如下语句(伪代码):
// 1. 将当前线程挂载到条件变量的等待队列cdt.add_to_queue(this_thread);// 2. 释放锁lck.unlock();// 3. 进入阻塞等待this_thread.wait();// -------------------// 4. 被其他线程唤醒lck.lock();
使用的例子
std::mutex mtx;std::condition_variable cond;void thread_A() {std::unique_lock<std::mutex> lock(mtx);std::cout << "wait" << std::endl;cond.wait(lock);std::cout << "awake" << std::endl;}void thread_B() {cond.notify_one();}int main() {std::thread tA(thread_A, nullptr);std::thread_tB(thread_B, nullptr);tA.join();tB.join();}
