互斥元
std::mutex
std::lock_guard
实现互斥元的 RAII 惯用语法,它在构造时锁定所给的互斥元,在析构时将互斥元解锁,从而保证被锁定的互斥元始终被正确解锁
std::unique_lock
提供了lock()、try_lock()和unlock()三个成员函数,它们会调用底层互斥元上同名的成员函数去做实际的工作,并且只是更新在std::unique_lock实例内部的一个标识,来表示该实例当前实例是否拥有此互斥元(如果标识表示了该实例拥有互斥元,则析构函数必须调用unlock();如果不拥有互斥元,则一定不能调用unlock())。因此std::unique_lock对象的大小通常大于std::lock_guard对象;使用std::unique_lock时,由于需要对标识进行相应的更新或判断在性能上会有些许损失
其灵活性还体现在可以在作用域之间转移锁的所有权
std::lock
待补充
加解锁的一些未定义行为
待补充
共享互斥元
boost::shared_mutex
boost::unique_lock<boost::shared_mutex>
写锁
boost::shared_lock<boost::shared_mutex>
读锁
递归互斥元
std::recursive_mutex
待补充
条件变量
从概念上说,条件变量与某些事件或其他条件相关,并且一个或多个线程可以等待该条件被满足。当某个线程已经确定条件得到满足,它就可以通知一个或多个正在条件变量上进行等待的线程,以便唤醒它们并让它们继续处理
std::condition_variable
等待操作
调用wait()函数需要传入锁对象,以及表示正在等待的条件的判断函数(判断函数可以使用lambda表达式)
调用wait()函数时,其内部首先会调用所提供的判断函数:
- 如果满足条件(lambda返回true)则返回,当前线程继续执行
- 如果不满足条件,则将当前线程置于等待状态(加入这个条件变量的等待队列)并解锁互斥元
当wait函数被唤醒时(其他线程调用通知或伪唤醒):
- 重新获取互斥元(加锁)
- 执行上面的一系列判断及相应操作
伪唤醒
当等待线程重新获取互斥元并检查条件时,如果它并非直接响应另一个线程的通知,即伪唤醒
丢失通知
在wait操作的实现流程的分析中,判断不满足条件与之后的所有处理并非是个原子操作,因此当前线程进入等待状态前,有可能其他线程的通知已经发出,这样就丢失了通知。具体实例可参考:线程池学习
std::condition_variable_any
灵活性更大,所以可能会有大小、性能或者操作系统资源方面的额外代价