开发者

Acquire a lock on two mutexes and avoid deadlock

The following code contains a potential deadlock, but seems to be necessary: to safely copy data to one container from another, both containers must be locked to prevent 开发者_C百科changes from occurring in another thread.

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

Foo has an STL container and "do copy" essentially consists of using std::copy. How do I lock both mutexes without introducing deadlock?


Impose some kind of total order on instances of foo and always acquire their locks in either increasing or decreasing order, e.g., foo1->lock() and then foo2->lock().

Another approach is to use functional semantics and instead write a foo::clone method that creates a new instance rather than clobbering an existing one.

If your code is doing lots of locking, you may need a complex deadlock-avoidance algorithm such as the banker's algorithm.


How about this?

void foo::copy(const foo & rhs)
{
    scopedLock lock(rhs.pMutex); // release mutex in destructor
    foo tmp(rhs);
    swap(tmp); // no throw swap locked internally
}

This is exception safe, and pretty thread safe as well. To be 100% thread save you'll need to review all code path and than re-review again with another set of eyes, after that review it again...


As @Mellester mentioned you can use std::lock for locking multiple mutexes avoiding deadlock.

#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
}

But note to check that rhs is not a *this since in this case std::lock will lead to UB due to locking same mutex.


this is a known problem already there is a std solution. std::lock() can be called on 2 or more mutex at the same time whilst avoiding deadlock's. More information here it does offer a recommendation.

std::scoped_lock offers a RAII wrapper for this function, and is generally preferred to a naked call to std::lock.

of course this doesn't really allow early releases of one lock above the other so use std::defer_lock or std::adopt_lock like I did in this answer to a similar question.


To avoid a deadlock its probably best, to wait until both resources can be locked:

Dont know which mutex API you are using so here is some arbitrary pseudo code, assume that can_lock() only checks if it can lock a mutex, and that try_lock() returns true if it did lock, and false, if the mutex is already locked by somebody else.

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
}


You can try locking both the mutexes at the same time using scoped_lock or auto_lock.... like bank transfer do...

void Transfer(Receiver recv, Sender send)
{
    scoped_lock rlock(recv.mutex);
    scoper_lock slock(send.mutex);

    //do transaction.
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜