C++ 获取两个互斥锁的锁并避免死锁
下面的代码包含一个潜在的死锁,但似乎是必要的:为了安全地将数据从一个容器复制到另一个容器,必须锁定两个容器,以防止在另一个线程中发生更改C++ 获取两个互斥锁的锁并避免死锁,c++,multithreading,deadlock,C++,Multithreading,Deadlock,下面的代码包含一个潜在的死锁,但似乎是必要的:为了安全地将数据从一个容器复制到另一个容器,必须锁定两个容器,以防止在另一个线程中发生更改 void foo::copy(const foo & rhs) { pMutex->lock(); rhs.pMutex->lock(); // do copy } Foo有一个STL容器,“docopy”本质上包括使用std::copy。如何在不引入死锁的情况下锁定两个互斥锁?对foo的实例施加某种总的顺序,并始
void foo::copy(const foo & rhs)
{
pMutex->lock();
rhs.pMutex->lock();
// do copy
}
Foo有一个STL容器,“docopy”本质上包括使用std::copy。如何在不引入死锁的情况下锁定两个互斥锁?对
foo
的实例施加某种总的顺序,并始终以递增或递减的顺序获取它们的锁,例如,foo1->lock()
然后foo2->lock()
另一种方法是使用函数语义,而不是编写一个foo::clone
方法来创建一个新实例,而不是关闭一个现有实例
如果您的代码正在执行大量锁定,您可能需要一个复杂的死锁避免算法,例如。这如何
void foo::copy(const foo & rhs)
{
scopedLock lock(rhs.pMutex); // release mutex in destructor
foo tmp(rhs);
swap(tmp); // no throw swap locked internally
}
这是异常安全的,也是线程安全的。要100%节省线程,您需要检查所有代码路径,然后用另一组眼睛再次检查,然后再次检查…为避免死锁,最好等到两个资源都可以锁定: 不知道您使用的是哪个互斥API,因此这里有一些任意的伪代码,假设
can\u lock()
只检查它是否可以锁定互斥体,如果它确实锁定,try\u lock()
则返回true,如果互斥体已经被其他人锁定,则返回false
void foo::copy(const foo & rhs)
{
for(;;)
{
if(! pMutex->cany_lock() || ! rhs.pMutex->cany_lock())
{
// Depending on your environment call or dont call sleep()
continue;
}
if(! pMutex->try_lock())
continue;
if(! rhs.pMutex->try_lock())
{
pMutex->try_lock()
continue;
}
break;
}
// do copy
}
您可以尝试使用作用域锁定或自动锁定同时锁定两个互斥锁。。。。像银行转账一样
void Transfer(Receiver recv, Sender send)
{
scoped_lock rlock(recv.mutex);
scoper_lock slock(send.mutex);
//do transaction.
}
这是一个已知的问题,已经有了std解决方案。
std::lock()
可以同时在两个或多个互斥体上调用,同时避免死锁。
它确实提供了一个建议
scoped_lock为这个函数提供了一个RAII包装,并且是
通常优先于对std::lock的裸调用
当然,这并不允许一个锁在另一个锁之上提前释放,所以使用
std::defer_lock
或std::adoption_lock
,就像我在这篇文章中对类似问题所做的那样。正如@Mellester提到的,您可以使用std::lock
锁定多个互斥锁,以避免死锁
#include <mutex>
void foo::copy(const foo& rhs)
{
std::lock(pMutex, rhs.pMutex);
std::lock_guard<std::mutex> l1(pMutex, std::adopt_lock);
std::lock_guard<std::mutex> l2(rhs.pMutex, std::adopt_lock);
// do copy
}
#包括
void foo::复制(const foo和rhs)
{
标准:锁(pMutex,rhs.pMutex);
标准:锁紧装置l1(pMutex,标准:采用锁紧装置);
标准:锁紧装置l2(rhs.pMutex,标准:采用锁紧装置);
//抄袭
}
但请注意,检查
rhs
不是*此
,因为在这种情况下,std::lock
会由于锁定相同的互斥锁而导致UB。即使是像此vs rhs地址这样简单的东西也可以工作。始终先锁定地址较低的那个。克隆只有在不复制的情况下才能正常工作,我认为隐式共享不起作用,但我会看一看。有趣的方法,凯尔。我看不出有任何缺陷。建议他制作一份数据的临时副本是一个很好的解决方案<然而,code>std::lock已经提供了这样一种死锁避免算法。为了避免死锁,最好引入一个活锁,这样会更具可读性吗?使用100%的CPU进行旋转?这是一种灾难的解决方法std::lock
有一个死锁避免算法将它传递给两个互斥体,并且它比实现自己的互斥体对其他互斥体更具可读性。您为什么不先使用std::unique\u lock
执行std::defer\u lock
,然后执行std::lock(l1,l2)代码>?还是仅仅是个人喜好/风格?