当开发人员在新范围内使用std::unique_lock
自动解锁互斥锁时,我看到了很多代码示例:
...
// do some staff
{
std::unique_lock<std::mutex> lock(shared_resource_mutex);
// do some actions with shared resource
}
// do some staff
...
我认为最好以以下方式使用unlock
API中的方法std::unique_lock
来实现此行为:
...
// do some actions
std::unique_lock<std::mutex> lock(shared_resource_mutex);
// do some actions with shared resource
lock.unlock();
// do some actions
...
这两个代码片段等效吗?开发人员出于什么目的使用第一个变体?也许强调(使用括号)不能并行执行的代码?
答案 0 :(得分:3)
在作用域末尾销毁对象时,将释放锁定。这就是RAII的重点。
使用RAII的好处是,您不会忘记解锁,也不管如何离开示波器。例如,如果引发异常,则锁仍将被释放。
如果您只需要在构造时锁定并在破坏时解锁,那么std::scoped_lock是一个更简单/更合适的类。
答案 1 :(得分:3)
我会说前一种方法更安全,更一致且更易于阅读。
首先考虑安全性:
void function()
{
std::unique_lock<std::shared_mutex> lock(mtx);
// exclusive lock stuff
lock.unlock();
// std::shared_lock<std::shared_mutex> lock(mtx); // whoops name in use
std::shared_lock<std::shared_mutex> lock2(mtx);
// read only shared lock stuff here
lock2.unlock(); // what if I forget to do this?
lock.lock(); // if I forgot to call lock2.unlock() undefined behavior
// back to the exclusive stuff
lock.unlock();
}
如果您要获取/释放的锁不同,而忘了打电话给unlock()
,那么您可能最终会尝试从锁中锁定相同的 mutex 两次。相同的线程。
这是未定义的行为,因此可能不会引起注意,但会引起麻烦。
如果在错误的锁变量上调用lock()
或unlock()
,该怎么办...(用lock2
而不是lock1
来说),可能性令人恐惧。
一致性:
此外,并非所有锁类型都具有.unlock()
函数(std::scoped_lock,std::lock_guard),因此最好与您的编码风格保持一致。
更易于阅读:
更容易看到哪些代码节使用了锁,这使得对代码的推理更加简单:
void function()
{
{
std::unique_lock<std::shared_mutex> lock(mtx);
// exclusive lock stuff
}
{
std::shared_lock<std::shared_mutex> lock(mtx);
// read only shared lock stuff here
}
{
std::unique_lock<std::shared_mutex> lock(mtx);
// back to the exclusive stuff
}
}
答案 2 :(得分:2)
这两种方法都是正确的,您可以根据情况选择其中一种。例如,当使用condition_variable / lock组合时,能够显式锁定和解锁该锁通常很有用。
这是我发现的又一种表达方式又很安全的方法:
#include <mutex>
template<class Mutex, class Function>
decltype(auto) with_lock(Mutex& m, Function&& f)
{
std::lock_guard<Mutex> lock(m);
return f();
}
std::mutex shared_resource_mutex;
void something()
{
with_lock(shared_resource_mutex, [&]
{
// some actions
});
// some other actions
}