Scoped_lock 重复参数
Scoped_lock with repeating arguments
我使用 std::scoped_lock 来保护多线程环境中的对象对。但是我发现如果 scoped_lock 的两个参数相同,它会导致死锁(在 Visual Studio 和 gcc 中)。例如,
#include <mutex>
struct S
{
mutable std::mutex m;
int v = 0;
S & operator = ( const S & b )
{
std::scoped_lock l( m, b.m );
v = b.v;
return * this;
}
};
int main()
{
S a;
a = a; //deadlock here!
}
我看到标准要求“如果 MutexTypes 之一不是递归互斥锁并且当前线程已经拥有......中的相应参数,则行为未定义”,请参阅
https://en.cppreference.com/w/cpp/thread/scoped_lock/scoped_lock
但在我的示例中,互斥量在 scoped_locked 之前没有被正式锁定。那么这是预期的程序行为吗?
你真正害怕的现象是“自死锁”。当同时满足 2 个条件时,它会发生:
- 同一个线程重复调用mutex.lock()互斥对象,
- 而且那个mutex属于非递归类型,不支持这种递归加锁
在 C++ 中有两种类型的互斥量:
- std::mutex 是非递归类型 - 它更快并且需要更少的资源,
- std::recursive_mutex 是递归类型 - 它更安全但需要更多资源。
现在让我们将这些知识应用到您的具体示例中:
在您的情况下,避免“自我死锁”的明显方法是避免“自赋值”。为此,只需添加额外的检查:
S & operator = ( const S & b )
{
if (this != &b) // check it is not self assignment
{
std::scoped_lock l( m, b.m );
v = b.v;
}
return * this;
}
如果这是您的 mutex 的唯一用法,您可以确定递归“self deadlocking”永远不会发生。所以,这是最好和最便宜的解决方案。
如果您要添加其他“同步”方法,当调用线程已经持有互斥量时调用当前方法,那么(并且仅在这种情况下)您确实需要替换 std::mutex 类型为 std::recursive_mutex.
我使用 std::scoped_lock 来保护多线程环境中的对象对。但是我发现如果 scoped_lock 的两个参数相同,它会导致死锁(在 Visual Studio 和 gcc 中)。例如,
#include <mutex>
struct S
{
mutable std::mutex m;
int v = 0;
S & operator = ( const S & b )
{
std::scoped_lock l( m, b.m );
v = b.v;
return * this;
}
};
int main()
{
S a;
a = a; //deadlock here!
}
我看到标准要求“如果 MutexTypes 之一不是递归互斥锁并且当前线程已经拥有......中的相应参数,则行为未定义”,请参阅 https://en.cppreference.com/w/cpp/thread/scoped_lock/scoped_lock
但在我的示例中,互斥量在 scoped_locked 之前没有被正式锁定。那么这是预期的程序行为吗?
你真正害怕的现象是“自死锁”。当同时满足 2 个条件时,它会发生:
- 同一个线程重复调用mutex.lock()互斥对象,
- 而且那个mutex属于非递归类型,不支持这种递归加锁
在 C++ 中有两种类型的互斥量:
- std::mutex 是非递归类型 - 它更快并且需要更少的资源,
- std::recursive_mutex 是递归类型 - 它更安全但需要更多资源。
现在让我们将这些知识应用到您的具体示例中:
在您的情况下,避免“自我死锁”的明显方法是避免“自赋值”。为此,只需添加额外的检查:
S & operator = ( const S & b ) { if (this != &b) // check it is not self assignment { std::scoped_lock l( m, b.m ); v = b.v; } return * this; }
如果这是您的 mutex 的唯一用法,您可以确定递归“self deadlocking”永远不会发生。所以,这是最好和最便宜的解决方案。
如果您要添加其他“同步”方法,当调用线程已经持有互斥量时调用当前方法,那么(并且仅在这种情况下)您确实需要替换 std::mutex 类型为 std::recursive_mutex.