题目链接:LeetCode 1114. 按序打印
// std::lock_guard 解法
class Foo {
public:
Foo() {
first_lock_.lock();
second_lock_.lock();
}
void first(function<void()> printFirst) {
// printFirst() outputs "first". Do not change or remove this line.
printFirst();
first_lock_.unlock();
}
void second(function<void()> printSecond) {
// printSecond() outputs "second". Do not change or remove this line.
lock_guard<mutex> lk(first_lock_);
printSecond();
second_lock_.unlock();
}
void third(function<void()> printThird) {
// printThird() outputs "third". Do not change or remove this line.
lock_guard<mutex> lk(second_lock_);
printThird();
}
private:
mutex first_lock_;
mutex second_lock_;
};
-
互斥类的最重要成员函数是 lock() 和 unlock() 。在进入临界区时,执行 lock() 加锁操作,如果这时已经被其它线程锁住,则当前线程在此排队等待。退出临界区时,执行 unlock() 解锁操作。
-
更好的办法是采用 ”资源分配时初始化” (RAII) 方法来加锁、解锁,这避免了在临界区中因为抛出异常或 return 等操作导致没有解锁就退出的问题。极大地简化了程序员编写 mutex 相关的异常处理代码。C++11的标准库中提供了std::lock_guard类模板做 mutex 的 RAII。
std::lock_guard 类的构造函数禁用拷贝构造,且禁用移动构造 std::lock_guard 类除了构造函数和析构函数外没有其它成员函数。
-
在 std::lock_guard 对象构造时,传入的 mutex 对象(即它所管理的 mutex 对象)会被当前线程锁住。在 lock_guard 对象被析构时,它所管理的 mutex 对象会自动解锁,不需要程序员手动调用 lock 和 unlock 对 mutex 进行上锁和解锁操作。
-
lock_guard 对象并不负责管理 mutex 对象的生命周期,lock_guard 对象只是简化了 mutex 对象的上锁和解锁操作,方便线程对互斥量上锁,即在某个 lock_guard 对象的生命周期内,它所管理的锁对象会一直保持上锁状态;而 lock_guard 的生命周期结束之后,它所管理的锁对象会被解锁。程序员可以非常方便地使用 lock_guard ,而不用担心异常安全问题。
-
std::lock_guard 在构造时只被锁定一次,并且在销毁时解锁。