为什么 C++ 并发性 listing_6.1 不使用 std::recursive_mutex
Why C++ concurrency in action listing_6.1 does not use std::recursive_mutex
我正在阅读 "C++ Concurrency In Action" 这本书,对清单 6.1 中使用的互斥体有一些疑问,代码片段如下:
void pop(T& value)
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw empty_stack();
value=std::move(data.top());
data.pop();
}
bool empty() const
{
std::lock_guard<std::mutex> lock(m);
return data.empty();
}
pop
方法锁定互斥体,然后调用空互斥体。但是互斥量不是 recursive_mutex,代码可以正常工作。所以我怀疑 std::mutex
和 std::recursive_mutex
.
之间的实际区别是什么
嗯,recursive_mutex
是为了...递归函数!
在某些操作系统中,两次锁定同一个互斥量会导致系统错误(其中,锁可能会被完全释放,应用程序可能会崩溃,实际上可能会发生各种奇怪和未定义的行为)。
看看这个(愚蠢的例子)
void recursivePusher(int x){
if (x>10){
return;
}
std::lock_guard<std::mutex> lock(m);
queue.push(x);
recursivePusher(x+1);
}
此函数递归递增 x
并将其推入某个共享 queue
。
正如我们上面所说的 - 同一个锁可能不会被同一个线程锁定两次,但我们确实需要确保共享队列不会被多个线程改变。
一个简单的解决方案是将定位移到递归函数之外,但如果我们做不到怎么办?如果调用的函数是唯一可以锁定共享资源的函数会怎样?
例如,我的调用函数可能如下所示:
switch(option){
case case1: recursivly_manipulate_shared_array(); break;
case case2: recursivly_manipulate_shared_queue(); break;
case case3: recursivly_manipulate_shared_map(); break;
}
当然,您不会只锁定所有三个(shred_Array、shared_map、shared_queue),因为其中一个会被更改。
解决方案是使用std::shared_mutex
:
void recursivePusher(int x){
if (x>10){
return;
}
std::lock_guard<std::recursive_mutex> lock(m);
queue.push(x);
recursivePusher(x+1);
}
如果同一个线程不需要递归地锁定互斥锁,它应该使用常规 std::mutex
,就像在您的示例中一样。
PS。在您的代码段中,empty
与 T::empty
不同。
调用 data.empty()
不会调用 empty
递归。
它正在调用 data.empty()
,这似乎是来自数据成员的函数。与您显示的 empty
功能不同。
如果是,这将是一个递归调用
bool empty() const
{
std::lock_guard<std::mutex> lock(m);
return data.empty();
}
什么都行不通。
我正在阅读 "C++ Concurrency In Action" 这本书,对清单 6.1 中使用的互斥体有一些疑问,代码片段如下:
void pop(T& value)
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw empty_stack();
value=std::move(data.top());
data.pop();
}
bool empty() const
{
std::lock_guard<std::mutex> lock(m);
return data.empty();
}
pop
方法锁定互斥体,然后调用空互斥体。但是互斥量不是 recursive_mutex,代码可以正常工作。所以我怀疑 std::mutex
和 std::recursive_mutex
.
嗯,recursive_mutex
是为了...递归函数!
在某些操作系统中,两次锁定同一个互斥量会导致系统错误(其中,锁可能会被完全释放,应用程序可能会崩溃,实际上可能会发生各种奇怪和未定义的行为)。
看看这个(愚蠢的例子)
void recursivePusher(int x){
if (x>10){
return;
}
std::lock_guard<std::mutex> lock(m);
queue.push(x);
recursivePusher(x+1);
}
此函数递归递增 x
并将其推入某个共享 queue
。
正如我们上面所说的 - 同一个锁可能不会被同一个线程锁定两次,但我们确实需要确保共享队列不会被多个线程改变。
一个简单的解决方案是将定位移到递归函数之外,但如果我们做不到怎么办?如果调用的函数是唯一可以锁定共享资源的函数会怎样?
例如,我的调用函数可能如下所示:
switch(option){
case case1: recursivly_manipulate_shared_array(); break;
case case2: recursivly_manipulate_shared_queue(); break;
case case3: recursivly_manipulate_shared_map(); break;
}
当然,您不会只锁定所有三个(shred_Array、shared_map、shared_queue),因为其中一个会被更改。
解决方案是使用std::shared_mutex
:
void recursivePusher(int x){
if (x>10){
return;
}
std::lock_guard<std::recursive_mutex> lock(m);
queue.push(x);
recursivePusher(x+1);
}
如果同一个线程不需要递归地锁定互斥锁,它应该使用常规 std::mutex
,就像在您的示例中一样。
PS。在您的代码段中,empty
与 T::empty
不同。
调用 data.empty()
不会调用 empty
递归。
它正在调用 data.empty()
,这似乎是来自数据成员的函数。与您显示的 empty
功能不同。
如果是,这将是一个递归调用
bool empty() const
{
std::lock_guard<std::mutex> lock(m);
return data.empty();
}
什么都行不通。