使用mutex
锁定关键代码区域的推荐方法是通过RAII,即
mutex_type mutex;
{ // start of critical region
std::lock_guard<mutex_type> lock(mutex); // first statement in critical region
// ... do critical stuff, may throw an exception
} // end of critical region
这样当在关键区域内抛出异常时,互斥锁仍将被解锁(由std::lock_guard
的析构函数)。但是,通过这种方式,成员mutex::lock()
和mutex::unlock()
永远不会被用户代码显式调用。
问 mutex::lock()
的主要惯用词是什么?
我问,否则让mutex::lock()
公共成员宣传不良代码(避免std::lock_guard
)会毫无意义。
修改
由于std::lock_guard<>
和std::mutex
都在同一标头中定义,因此std::mutex
可以很容易地与std::lock_guard<std::mutex
成为朋友并保护其lock()
和unlock()
方法:
class mutex // use only with lock_guard<mutex>
{
friend class lock_guard<mutex>; // support acquire-release semantic via RAII
friend class scoped_lock_guard<mutex>; // for supporting more complicated semantic,
// possibly remembering the mutex state.
// ...
protected:
void lock();
bool try_lock();
void unlock();
};
class raw_mutex // use if you absolutely must explicitly lock, try_lock, or unlock
: public mutex
{
public:
using mutex::lock;
using mutex::try_lock;
using mutex::unlock;
};
回答我的问题的一个论点就是使用mutex::lock()
的唯一例外安全方法是通过RAII。因此,唯一合理的显式使用必须仅涉及noexcept
(或lock
)和try_lock
的调用之间的unlock
方法。但是,由于noexcept
仅仅是暗示性的,并且没有任何承诺,因此这种使用是不安全的。 Q 正确吗?
答案 0 :(得分:4)
lock_guard
不是唯一需要在lock
上致电unlock
/ mutex
的人。 unique_lock
,lock
,try_lock
和condition_variable_any
都必须处理互斥锁。这只是标准类型。在这种情况下,友谊引入了紧密耦合,成为一种障碍。
答案 1 :(得分:1)
当存在可以绑定资源生命周期的静态作用域时,可以(并且应该)使用RAII,即在输入作用域时应该初始化资源,并在退出作用域时释放。
但是,有些情况下,没有这样的静态范围。 为了说明这一点,请将变量视为资源。当存在我们可以绑定它们的静态范围时,我们使用自动变量,但偶尔我们需要动态变量来控制它们何时被创建和销毁。
当我们使用mutex
es进行互斥时,也会发生同样的情况。考虑这个任务:
您的程序控制两个资源,它必须读取并执行一系列命令。可能的命令是:
1>
2>
1<
2<
x
写入资源#1:1x
x
写入资源#2:2x
您的程序可以预期输入1>
1a
2>
2b
1c
1<
2d
2<
,
如果程序服从命令(范围必须部分重叠),则无法使用RAII的静态范围。
所以我认为这说明了当需要显式锁定/解锁时的一种情况。
至少有一种可能的情况来自于互斥不是唯一可以使用mutex
的情况:它也可以用于同步。
考虑两个并行流程P
和Q
,每个流程在其代码中都有一个指定点。我们要求Q
无法通过指定点,直到P
到达自己的位置。使用mutex
初始化为locked
州并在lock()
的指定点和Q
之后立即执行unlock()
操作,即可满足此要求P
的指定点。可以很容易地验证此设置可以解决问题。
此处lock()
放在一个进程中,而unlock()
放在另一个进程中,显然没有静态范围可以包含它们,所以我们需要两个都可以独立访问。