1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
A deadlock occurs when two locks are required to process data, but one thread
obtains the first lock and another thread obtains the second lock. bf(C++)
defines the generic
hi(lock)tt(std::lock) and hi(try_lock)tt(std::try_lock) functions that can
be used to help preventing such situations.
Before these functions can be used the tthi(mutex) header file must be
included
In the following overview tt(L1 &l1, ...) represents one or more
references to objects of lockable types:
itemization(
ittq(void std::lock(L1 &l1, ...))
(When the function returns locks were obtained on all tt(li)
objects. If a lock could not be obtained for at least one of the
objects, then all locks obtained so far are relased, even if the
object for which no lock could be obtained threw an exception;)
ittq(int std::try_lock(L1 &l1, ...))
(This function calls the lockable objects' tt(try_lock) members. If all
locks could be obtained, then -1 is returned. Otherwise the (0-based)
index of the first argument which could not be locked is returned,
releasing all previously obtained locks.)
)
As an example consider the following little multi-threaded program: The
threads use mutexes to obtain unique access to tt(cout) and to an tt(int
value). However, tt(fun1) first locks tt(cout) (line 7), and then tt(value)
(line 10); tt(fun2) first locks tt(value) (line 16) and then tt(cout) (line
19). Clearly, if tt(fun1) has locked tt(cout) tt(fun2) can't obtain the lock
until tt(fun1) has released it. Unfortunately, tt(fun2) has locked tt(value),
and the functions only release their locks when returning. But in order to
access the information in tt(value) tt(fun1) it must have obtained a lock on
tt(value), which it can't, as tt(fun2) has already locked tt(value): the
threads are waiting for each other, and neither thread gives in.
verbinclude(-ns4 //code examples/deadlock.cc)
A good recipe for avoiding deadlocks is to prevent nested (or multiple) mutex
lock calls. But if multiple mutexes must be used, always obtain the locks in
the same order. Rather than doing this yourself, tt(std::lock) and
tt(std::try_lock) should be used whenever possible to obtain multiple mutex
locks. These functions accept multiple arguments, which must be lockable types
like tt(lock_guard, unique_lock,) or even a plain tt(mutex). The previous
deadlocking program, can be modified to call tt(std::lock) to lock both
mutexes. In this example using one single mutex would also work, but the
modified program now looks as similar as possible to the previous
program. Note how in lines 10 and 21 a different
ordering of the tt(unique_locks) arguments was used: it is not necessary to
use an identical argument order when calling tt(std::lock) or
tt(std::try_lock).
verbinclude(-ns4 //code examples/lock.cc)
|