The common solution to preventing deadlock in code is to make sure the sequence of locking occur in a common manner regardless of which thread is accessing the resources.
For example given threads T1 and T2, where T1 accesses resource A and then B and T2 accesses resource B and then A. Locking the resources in the order they are needed causes a dead-lock. The simple solution is to lock A and then lock B, regardless of the order specific thread will use the resources.
Problematic situation:
Thread1 Thread2
------- -------
Lock Resource A Lock Resource B
Do Resource A thing... Do Resource B thing...
Lock Resource B Lock Resource A
Do Resource B thing... Do Resource A thing...
Possible Solution:
Thread1 Thread2
------- -------
Lock Resource A Lock Resource A
Lock Resource B Lock Resource B
Do Resource A thing... Do Resource B thing...
Do Resource B thing... Do Resource A thing...
My question is what other techniques, patterns or common practices are used in coding to guarantee dead lock prevention?
The technique you describe isn't just common: it's the one technique that has been proven to work all the time. There are a few other rules you should follow when coding threaded code in C++, though, among which the most important may be:
I could go on for a while, but in my experience, the easiest way to work with threads is using patterns that are well-known to everyone who might work with the code, such as the producer/consumer pattern: it's easy to explain and you only need one tool (a queue) to allow your threads to communicate with each other. After all, the only reason for two threads to be synchronized with each other, is to allow them to communicate.
More general advice:
#include <thread>
#include <cassert>
#include <chrono>
#include <iostream>
#include <mutex>
void
nothing_could_possibly_go_wrong()
{
int flag = 0;
std::condition_variable cond;
std::mutex mutex;
int done = 0;
typedef std::unique_lock<std::mutex> lock;
auto const f = [&]
{
if(flag == 0) ++flag;
lock l(mutex);
++done;
cond.notify_one();
};
std::thread threads[2] = {
std::thread(f),
std::thread(f)
};
threads[0].join();
threads[1].join();
lock l(mutex);
cond.wait(l, [done] { return done == 2; });
// surely this can't fail!
assert( flag == 1 );
}
int
main()
{
for(;;) nothing_could_possibly_go_wrong();
}