
Kowalke Oliver wrote: what's the best practice in following scenario: m threads and n resources (each resource is protected by a mutex) thread x needs read/write access to resources (...i,j,k,..) threads y,z,... do access another subset of resources at the same time
inorder to protected deadlocks I would do following in the code of the different threads (code executed by the threads are different):
... try_lock: // label unique_lock< mutex > lk1( mtx1, try_to_lock_t); unique_lock< mutex > lk4( mtx2, try_to_lock_t); unique_lock< mutex > lk7( mtx3, try_to_lock_t); if ( ! ( lk1 && lk2 && lk3) ) goto try_lock; // all mutexes are locked // now modify safely resource 1,2,3 ...
Maybe you have a better pattern do solve this problem?
N2447 proposes a generic locking algorithm. Quote: template <class L1, class L2, class ...L3> void lock(L1&, L2&, L3&...); Requires: Each template parameter type must supply the following member functions with semantics corresponding to the Mutex concept, except that try_lock is allowed to throw an exception [Note: The unique_lock class template meets these requirements when suitable instantiated. --end note] void lock(); bool try_lock(); void unlock(); Effects: All arguments are locked with an algorithm that avoids deadlock. If an exception is thrown by a call to lock() or try_lock(), there are no effects. End Quote. Presumably the intention is that you use it like this: { unique_lock< mutex > lk1( mtx1, defer_lock); unique_lock< mutex > lk4( mtx2, defer_lock); unique_lock< mutex > lk7( mtx3, defer_lock); lock(lk1,lk2,lk3); ....critical section.... } // locks are released when they go out of scope I guess that a multi-lock could be built on top of this, for more concise code: struct multilock { unique_lock lk1; unique_lock lk2; unique_lock lk3; multilock(mutex& mtx1, mutex& mtx2, mutex& mtx3): lk1(mtx1, defer_lock), lk2(mtx2, defer_lock), lk3(mtx3, defer_lock) { lock(lk1,lk2,lk3); } }; { multilock mlk(mtx1,mtx2,mtx3); ...critical section.... } This is not included in Boost.Threads (AFAIK), and doing so without C++0x varargs templates is a bit messy. The obvious (to me!) implementation of the lock algorithm is to order the mutexes. This can be done based on their addresses, if every instance of the same underlying mutex has the same address. This doesn't work for shared-memory mutexes; maybe the mutex itself should be required to be less-than-comparable? Anyway, you can easily write a version for a fixed number of locks: void lock(lock& lk1, lock& lk2) { if (lk1.mutex() < lk2.mutex()) { lk1.lock(); lk2.lock(); } else { lk2.lock(); lk1.lock(); } } Phil.