Monday 8 June 2015

C++11 Features - Mutex and Deadlock

This is more or less a reading note of Chapter 3 of "Concurrency IN ACTION - Practical Multithreading" by Anthony Williams. As I have discussed threading - deadlock and how to ensure one instance in singleton in my previous blog entries, I cherry-pick up a few things that would be beneficial for these two issues in C++11 features.

1. std::lock_guard and std::mutex
This is one of C++ RAII techniques to solve one serious issue. I have discussed a few concrete scenarios that use RAII to prevent serious issues like memory leak, left open file handler and etc in this long entry, RAII.

We are facing the same dangers when designing multithreading code, for instance not unlocking the mutex in some corner code path or not unlocking the mutex when exception thrown. Unlocking the mutex will surely cause deadlock when try to acquire mutex in the next call. std::lock_guard is the solution to prevent these issues. It uses RAII techniques and the mutex associated with it will be automatically unlocked when std::lock_guard object goes out of scope.

C++11 also provides a few variants that are combined with other C++11 features. std::unique_lock to combine with the std::move feature and std::shared_lock to combine the resource management and shared owership.

2. std::shared_mutex and std::shared_lock
This pair are perfect for reader-writer mutex. If anyone familiar with QT mutex or boost mutex, understanding them is not a big deal. It is perfectly designed for one writer thread and multiple reader threads. In order to remove the bottleneck of reading threads, it should just give green lights to reader threads if shared resource is not acquired by the writer thread. Otherwise wait until the writer thread finishes its business.

3. std::lock
This is the biggest cherry I found in this chapter. I have discussed how to avoid deadlock of multiple level/nested locking access in Threading - Deadlock. The techniques are to avoid the circle locking in the design stage and to always lock multiple mutexes in the certain order.

std::lock provides a solution to lock multiple mutexes all-in-once. It takes N locks as parameters and always lock them at once. It is a nothing-or-all operation. Either all N locks are locked at once or none of them is locked if attempt fail.

4. std::call_once
I have discussed the difficulty that we are facing to make sure the uniqueness of singleton in Design pattern - Singleton pattern. Now C++11 provides the exact tool to achieve this. It guarantees that the function passed into it will be only invoked once in multithreading applications.

It is particular important for lazy initialization of C++. Multiple threads race to initialize the data shared between them when they first time come into use. std::call_once will make the lazy initialization in multithreading much easier to be done.

5. Other thing worth mentioning
Still no solution to the deadlock caused by the mismanagement of threading. For instance two worker threading waiting for the counterpart to join. An excellent example (linked list operation) is provided to explain that design thread-safe code is not about the function itself but also its API design.

No comments:

Post a Comment