You need to consider many issues when you want to write concurrent code. Concurrent programming need good skills in programming. For your case(linked list) you need to consider:
- Exception safe code: When an exception has thrown in your code you need to ensure that all locked
mutex
s be unlocked.
- Avoid dead lock: You must lock
mutex
s in an order that doesn't put your code in dead lock state.
- Avoid race conditions: Race condition occur when two or more thread try to access shared data without any proper synchronization.
Let came back to your example.(I assume that your are using C++11
)
Exception safe code: you must always use std::lock_guard or std::unique_lock when you want to use std::mutex
:
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard
lock a mutex
in it's constructor and unlock it in it's destruction. This pattern is RAII technique. So whenever your code throws an exception you are sure that all mutex
s will be unlocked automatically.
Dead lock problem: In your specific case you need to choose an order in your class when you lock multiple mutex. If in one function of your class you lock before mutex
and then after mutex
, in all functions you must follow this order. I mean you can't lock after mutex
first and then before mutex
. If you don't follow a specific order in your locking pattern you will ended with dead lock.
Race condition problem: In your code you have locked two mutex
separately. Consider this example:
One thread enter into insert
function and before executing after->mtx.lock();
give it's time slice to another thread. Until now your list would look like bellow state:

Right at this moment another thread enter insert
function to add another node right at the same place. After this thread return from the function state of list will be equal to:

And now first thread continue it's work until it return, so change list as:

Now you have a broken list. This is data race in your code.
proper way to implement insertion is:
void insert(Node* before, unsigned value)
{
Node* node = new Node;
node->loadValue = value;
std::lock_guard<std::mutex> before_guard(before->mtx);
Node* after = before->next;
std::lock_guard<std::mutex> after_guard(after->mtx);
before->next = node;
after->prev = node;
node->prev = before;
node->next = after;
}
An advise: Try to don't write concurrent containers by yourself. It's a professional domain and need much experience to come up with right solution. There is concurrent library like Intel TBB that you can use