以下代码包含潜在的死锁,但似乎是必要的:要将数据安全地从另一个容器复制到一个容器,必须锁定这两个容器以防止在另一个线程中发生更改。
void foo::copy(const foo & rhs)
{
pMutex->lock();
rhs.pMutex->lock();
// do copy
}
Foo有一个STL容器,“do copy”主要包括使用std :: copy。如何在不引入死锁的情况下锁定两个互斥锁?
答案 0 :(得分:15)
对foo
的实例施加某种总排序,并始终以递增或递减顺序获取其锁定,例如,foo1->lock()
然后foo2->lock()
另一种方法是使用函数语义,而是编写一个foo::clone
方法来创建一个新实例而不是破坏现有实例。
如果您的代码执行大量锁定,则可能需要一个复杂的死锁避免算法,例如banker's algorithm。
答案 1 :(得分:1)
这个怎么样?
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%线程保存,您需要检查所有代码路径,然后再用另一组眼睛重新审核,然后再次审核...
答案 2 :(得分:0)
这是一个已知问题,已经有一个标准解决方案。
std::lock()
可以同时在2个或更多互斥锁上调用,同时避免死锁。
More information here
它确实提供了建议。
std :: scoped_lock为此功能提供RAII包装器,并且 通常首选裸露std :: lock。
当然,这实际上并不能真正允许较早释放一个锁,因此请像我在此answer中所做的那样,使用std::defer_lock
或std::adopt_lock
来解决类似的问题。
答案 3 :(得分:0)
正如@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
}
但是请注意检查rhs
不是*this
,因为在这种情况下,std::lock
由于锁定相同的互斥锁而将导致UB。
答案 4 :(得分:-1)
为了避免死锁,可能最好,等到两个资源都可以锁定:
不知道你正在使用哪个互斥API,所以这里有一些任意伪代码,假设can_lock()
只检查它是否可以锁定互斥锁,try_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
}
答案 5 :(得分:-2)
您可以尝试使用scoped_lock或auto_lock来同时锁定两个互斥锁....就像银行转帐一样......
void Transfer(Receiver recv, Sender send)
{
scoped_lock rlock(recv.mutex);
scoper_lock slock(send.mutex);
//do transaction.
}