gpt4 book ai didi

c++ - std::mutex 与 std::recursive_mutex 作为类成员

转载 作者:IT老高 更新时间:2023-10-28 14:00:35 49 4
gpt4 key购买 nike

我看到有人讨厌 recursive_mutex:

http://www.zaval.org/resources/library/butenhof1.html

但是当考虑如何实现一个线程安全的类(互斥保护)时,在我看来很难证明每个应该受互斥保护的方法都是互斥保护的,并且互斥最多被锁定一次。

所以对于面向对象的设计,应该 std::recursive_mutex 是默认的,而 std::mutex 在一般情况下被认为是一种性能优化,除非它只用于一个地点(只保护一种资源)?

为了清楚起见,我说的是一个私有(private)的非静态互斥体。所以每个类实例只有一个互斥体。

在每个公共(public)方法的开头:

{
std::scoped_lock<std::recursive_mutex> sl;

最佳答案

大多数时候,如果您认为需要递归互斥锁,那么您的设计就是错误的,因此绝对不应该是默认设置。

对于具有单个互斥锁保护数据成员的类,互斥锁应锁定在所有 public 成员函数中,并且所有 private 成员函数应假定互斥体已被锁定。

如果一个public成员函数需要调用另一个public成员函数,则将第二个一分为二:一个private实现函数做这项工作,以及一个 public 成员函数,它只是锁定互斥体并调用 private 之一。然后第一个成员函数也可以调用实现函数,而不必担心递归锁定。

例如

class X {
std::mutex m;
int data;
int const max=50;

void increment_data() {
if (data >= max)
throw std::runtime_error("too big");
++data;
}
public:
X():data(0){}
int fetch_count() {
std::lock_guard<std::mutex> guard(m);
return data;
}
void increase_count() {
std::lock_guard<std::mutex> guard(m);
increment_data();
}
int increase_count_and_return() {
std::lock_guard<std::mutex> guard(m);
increment_data();
return data;
}
};

这当然是一个简单的人为示例,但是 increment_data 函数在两个公共(public)成员函数之间共享,每个成员函数都锁定互斥体。在单线程代码中,可以内联到increase_countincrease_count_and_return可以调用它,但是在多线程代码中我们不能这样做。

这只是良好设计原则的应用:公共(public)成员函数负责锁定互斥体,并将完成工作的责任委托(delegate)给私有(private)成员函数。

这样做的好处是 public 成员函数只需要处理在类处于一致状态时被调用:互斥锁被解锁,一旦它被锁定,那么所有不变量都成立。如果您互相调用 public 成员函数,那么它们必须处理互斥锁已被锁定且不变量不一定成立的情况。

这也意味着条件变量等待之类的事情将起作用:如果您将递归互斥锁上的锁传递给条件变量,那么(a)您需要使用 std::condition_variable_any 因为 std::condition_variable 不起作用,并且 (b) 只释放了一级锁,所以你可能仍然持有锁,因此死锁,因为触发谓词并执行通知的线程不能获取锁。

我很难想到需要递归互斥锁的场景。

关于c++ - std::mutex 与 std::recursive_mutex 作为类成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14498892/

49 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com