获取两个互斥锁的锁定,避免死锁

时间:2010-02-12 03:53:35

标签: c++ multithreading deadlock

以下代码包含潜在的死锁,但似乎是必要的:要将数据安全地从另一个容器复制到一个容器,必须锁定这两个容器以防止在另一个线程中发生更改。

void foo::copy(const foo & rhs)
{
    pMutex->lock();
    rhs.pMutex->lock();
    // do copy
}

Foo有一个STL容器,“do copy”主要包括使用std :: copy。如何在不引入死锁的情况下锁定两个互斥锁?

6 个答案:

答案 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_lockstd::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.
}