Acquire a lock on two mutexes and avoid deadlock

前端 未结 6 1146
清歌不尽
清歌不尽 2021-01-04 13:04

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 cha

相关标签:
6条回答
  • 2021-01-04 13:48

    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.

    0 讨论(0)
  • 2021-01-04 13:49

    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...

    0 讨论(0)
  • 2021-01-04 13:52

    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
    }
    
    0 讨论(0)
  • 2021-01-04 13:54

    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 讨论(0)
  • 2021-01-04 13:59

    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.

    0 讨论(0)
  • 2021-01-04 14:00

    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.

    0 讨论(0)
提交回复
热议问题