为什么行为中的C ++并发性list_6.1不使用std :: recursive_mutex

时间:2015-10-13 09:07:51

标签: c++ c++11 std mutex 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::mutexstd::recursive_mutex之间的实际区别是什么。

2 个答案:

答案 0 :(得分:4)

它正在调用data.empty(),它似乎是来自数据成员的函数。与您显示的empty功能不同。

如果是,这将是一个递归调用

bool empty() const
{
    std::lock_guard<std::mutex> lock(m);
    return data.empty();
}

并没有任何效果。

答案 1 :(得分:3)

好吧,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。在您的代码段中,emptyT::empty不同。 调用data.empty()不会调用empty recursivley。