mirror of
https://gitlab.com/fbb-git/cppannotations
synced 2024-11-16 07:48:44 +01:00
wip on deadlocks.yo
This commit is contained in:
parent
823960b567
commit
5f186d6744
2 changed files with 86 additions and 7 deletions
|
@ -1,10 +1,50 @@
|
|||
Although they should be avoided, hi(deadlock) em(Deadlocks) are frequently
|
||||
encountered in multi threaded programs. 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. The C++11 standard defines a generic
|
||||
hi(lock)tt(std::lock) function that can be used to help preventing
|
||||
such situations. The tt(std::lock) function can be used to lock multiple
|
||||
mutexes in one atomic action. Here is an example:
|
||||
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. The C++11
|
||||
standard defines the generic
|
||||
hi(lock)tt(std::lock) and hi(try_lock)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:
|
||||
ittq(void std::lock(L1 &l1, ...))
|
||||
quote(
|
||||
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, ...))
|
||||
quote(
|
||||
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 small multi-threaded program: The threads
|
||||
use mutexes to obtain unique access to tt(cout) and 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 recipe to aWhen using multiple locks
|
||||
These functions can be
|
||||
used to lock multiple mutexes in one atomic action. Here is an example:
|
||||
verb(
|
||||
struct SafeString
|
||||
{
|
||||
|
|
39
annotations/yo/threading/examples/deadlock.cc
Normal file
39
annotations/yo/threading/examples/deadlock.cc
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
using namespace std;
|
||||
|
||||
//code
|
||||
|
||||
int value;
|
||||
mutex valueMutex;
|
||||
mutex coutMutex;
|
||||
|
||||
void fun1()
|
||||
{
|
||||
lock_guard<mutex> lg1(coutMutex);
|
||||
cout << "fun 1 locks cout\n";
|
||||
|
||||
lock_guard<mutex> lg2(valueMutex);
|
||||
cout << "fun 1 locks value\n";
|
||||
}
|
||||
|
||||
void fun2()
|
||||
{
|
||||
lock_guard<mutex> lg1(valueMutex);
|
||||
cerr << "fun 2 locks value\n";
|
||||
|
||||
lock_guard<mutex> lg2(coutMutex);
|
||||
cout << "fun 2 locks cout\n";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
thread t1(fun1);
|
||||
fun2();
|
||||
t1.join();
|
||||
}
|
||||
|
||||
//=
|
||||
|
Loading…
Reference in a new issue