描述
- 头文件
<mutex>
- 使用
std::mutex <variable>
- 简介
mutex
是一种多线程变成中的同步原语,它能够让共享数据不被多个线程同时访问,它不支持递归得对互斥对象上锁 - 特点
- 用方线程从它成功调用 lock 或 try_lock 开始,到它调用 unlock 为止占有 mutex
- 线程占有 mutex 时,所有其他线程若试图要求 mutex 的所有权,则将阻塞(对于 lock 的调用)或收到 false 返回值(对于 try_lock )
- 调用方线程在调用 lock 或 try_lock 前必须不占有 mutex ,否则无法获取<mutex>变量
成员函数
-
构造函数
std::mutex::mutex
constexpr mutex() noexcept;(1) mutex( const mutex& ) = delete;(2)
(1)构造mutex实例,
mutex
实例构造完成之后是处于unlocked
状态
(2)mutex的拷贝构造函数是不存在的 -
std::mutex::~mutex
析构函数
销毁互斥变量
若互斥为任何线程占有,或若任何线程在保有任何互斥的所有权时终止,则行为未定义 -
赋值运算符,不存在
-
std::mutex::lock
锁定互斥。若另一线程已锁定互斥,则到 lock 的调用将阻塞执行,直至获得锁
错误发生时抛出 std::system_error ,包括底层操作系统阻止 lock 满足其规定的错误。在抛出任何异常的情况下,不锁定互斥。所以,直接使用mutex的lock成员是无处法处理异常的情况。#include <iostream> #include <chrono> #include <thread> #include <mutex> int g_num = 0; // protected by g_num_mutex 共享数据 std::mutex g_num_mutex; void slow_increment(int id) { for (int i = 0; i < 3; ++i) { g_num_mutex.lock(); ++g_num; std::cout << id << " => " << g_num << '\n'; g_num_mutex.unlock(); //chrono是C++的时间库,提供1s线程的休眠 std::this_thread::sleep_for(std::chrono::seconds(1)); } } int main() { //0号线程和1号线程交叉执行对全局变量g_num对累加操作,执行完成之后sleep 1秒 std::thread t1(slow_increment, 0); std::thread t2(slow_increment, 1); t1.join(); t2.join(); }
输出如下
0 => 1 1 => 2 0 => 3 1 => 4 0 => 5 1 => 6
-
std::mutex::try_lock
尝试锁定互斥。立即返回。成功获得锁时返回 true ,否则返回 false。
若此操作返回 true ,则同一互斥上的先前 unlock() 操作同步于(定义于 std::memory_order )它。注意若此操作返回 false ,则先前的 lock() 不与之同步#include <chrono> #include <mutex> #include <thread> #include <iostream> // std::cout std::chrono::milliseconds interval(100); std::mutex mutex; int job_shared = 0; // 两个线程都能修改 'job_shared', // mutex 将保护此变量 int job_exclusive = 0; // 只有一个线程能修改 'job_exclusive' // 不需要保护 // 此线程能修改 'job_shared' 和 'job_exclusive' void job_1() { std::this_thread::sleep_for(interval); // 令 'job_2' 持锁 while (true) { // 尝试锁定 mutex 以修改 'job_shared' if (mutex.try_lock()) { std::cout << "job shared (" << job_shared << ")\n"; mutex.unlock(); return; } else { // 不能获取锁以修改 'job_shared' // 但有其他工作可做 ++job_exclusive; std::cout << "job exclusive (" << job_exclusive << ")\n"; std::this_thread::sleep_for(interval); } } } // 此线程只能修改 'job_shared' void job_2() { mutex.lock(); std::this_thread::sleep_for(5 * interval); ++job_shared; mutex.unlock(); } int main() { /* 可以看到job1中的实现,使用的是try_lock获取锁,因为刚开始job1线程 会进行100毫秒的休眠,所以cpu会先去执行job2,但是job2使用的是lock成员 所以在job2中sleep 500毫秒的过程中执行job1时try_lock返回false,则执行exclusive输出. 当job2获取不到锁,job1休眠结束之后释放锁,则job1重新获取锁成功 */ std::thread thread_1(job_1); std::thread thread_2(job_2); thread_1.join(); thread_2.join(); }
输出如下
job exclusive (1) job exclusive (2) job exclusive (3) job exclusive (4) job shared (1)
-
std::mutex::unlock
解锁mutex实例
使用前提是当前线程必须被mutex的实例锁定,否则改函数的调用是未定义的
注意,当前成员与lock()
成员,一般不直接调用,因为对异常情况的处理并不友好(程序锁定期间发生异常,进程就直接退出),所以一般与std::unique_lock 与 std::lock_guard 管理排他性锁 一起使用
具体使用实例可以参考如上两个代码。
总结
mutex互斥变量,并提供来了独占锁特性。为C++提供了多线程访问共享数据的保护措施,同时引入了像unique_lock
和lock_gurad
异常处理锁机制来规避mutex
的成员处理异常情况的不足。