为什么c++并发操作listing_6.1不使用std::recursive_mutex

Why C++ concurrency in action listing_6.1 does not use std::recursive_mutex

本文关键字:mutex std recursive 并发 c++ 操作 listing 为什么      更新时间:2023-10-16

我正在阅读《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方法锁定互斥锁,然后调用空互斥锁。但是这个互斥锁不是一个递归互斥锁,代码可以正常工作。所以我怀疑std::mutexstd::recursive_mutex到底有什么区别

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

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

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

好吧,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,就像你的例子一样。

p。在代码片段中,emptyT::empty不同。调用data.empty()不会递归地调用empty