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();
}