1. Overview
Lock type
C11 provides cross-platform thread synchronization to protect shared data that is accessed concurrently by multiple threads.
Std::mutex, the most basic mutex class, exclusive mutex, cannot be used recursively.
Std::time_mutex, with a timeout exclusive mutex, cannot be used recursively.
Std::recursive_mutex, recursive mutex, without timeout function.
Std::recursive_timed_mutex, with a recursive mutex with a timeout.
Lock type
Std::lock_guard, associated with the mutex RAII, facilitates the thread to lock the mutex.
Std::unique_lock, which is related to the mutex RAII, facilitates the thread to lock the mutex, but provides better locking and unlocking control.
Lock function
Std::try_lock, try to lock multiple mutexes simultaneously.
Std::lock, you can lock multiple mutexes at the same time.
Std::unlock, Unlock.
2, exclusive mutex amount Std::mutex
Mutex usage is simple, and interface usage is simple. Typically, the thread is blocked by lock () until it obtains the mutex amount. After the mutex is obtained, use unlock () to disassociate the mutex. Lock () and unlock () must appear in pairs.
Std::mutex does not allow copy construction, nor does it allow move copies, which are initially generated by the mutex object in the unlocked state.
Lock (), the calling thread will lock the mutex. The following 3 scenarios occur when a thread calls this function: (1). If the mutex is not currently locked, the calling thread locks the mutex until the unlock is called before the thread has the lock. (2). If the current mutex is locked by another thread, the current calling thread is blocked. (3). If the current mutex is locked by the current calling thread, a deadlock (deadlock) is generated.
Unlock (), unlocks, releases ownership of the mutex.
Try_lock (), attempts to lock the mutex, and if the mutex is occupied by another thread, the current thread will not be blocked. The following 3 scenarios are also present in the thread invocation of the function (1). If the current mutex is not occupied by another thread, the thread locks the mutex until the thread calls unlock to release the mutex. (2). If the current mutex is locked by another thread, the current calling thread returns false and is not blocked. (3). If the current mutex is locked by the current calling thread, a deadlock (deadlock) is generated.
#include <iostream>#include<thread>#include<chrono>#include<mutex>Std::mutex G_lock;voidVfunc () {g_lock.Lock(); Std::cout<<"entered thread:"<< std::this_thread::get_id () <<Std::endl; Std::this_thread::sleep_for (Std::chrono::seconds (1)); Std::cout<<"Leave thread:"<< std::this_thread::get_id () <<Std::endl; G_lock.unlock ();}intMain () {Std::thread T (vfunc); Std::thread T1 (Vfunc); Std::thread T2 (VFUNC); T.join (); T1.join (); T2.join (); return 0;}//Output ResultsEntered thread:4420Leave thread:4420entered thread:5288Leave thread:5288entered thread:1432Leave thread:1432
3, Std::lock_guard and Std::unique_lock
The use of Lock_guard and Std::unique_lock simplifies the writing of Lock/unlock and is also more secure, because Lock_guard uses RAII technology, allocating resources in the construction, releasing resources in the destruction, and automatically locking the mutex at the time of construction. After exiting the scope, the destructor is automatically unlocked. So do not worry about not unlocking the situation, more secure.
Std::lock_guard is related to the mutex RAII, facilitates the thread to lock the mutex, Std::unique_lock is related to the mutex RAII, facilitates the thread to lock the mutex, but provides better lock and unlock control, it can release the mutex freely, and Std::lock_guard need to wait until the end of life cycle to release.
void Vfunc () { std::lock_guard<std::mutex> Locker (g_lock); out-of-scope auto-unlock "entered thread:" << std::this_thread::get_id () < < Std::endl; Std::this_thread::sleep_for (Std::chrono::seconds (1)); " Leave thread: " << std::this_thread::get_id () << Std::endl;}
3, recursive mutual exclusion lock Std::recuisive_mutex
The same thread cannot get the same exclusive mutex multiple times, and one thread acquires the same mutex multiple times with a deadlock.
//Std::mutex Mutex;voidFunca () {Std::lock_guard<std::mutex>Lock(mutex); //Do something}voidFUNCB () {Std::lock_guard<std::mutex>Lock(mutex); //Do something}voidFUNCC () {Std::lock_guard<std::mutex>Lock(mutex); Funca (); FUNCB ();}intMain () {//A deadlock has occurred, FUNCC has been acquired, cannot be released, Funca cannot getFUNCC (); return 0;}
To solve the deadlock problem, C11 has a recursive lock Std::recursive_mutex, which allows a thread to acquire the lock multiple times.
//Std::recursive_mutex Mutex;voidFunca () {Std::lock_guard<std::recursive_mutex>Lock(mutex); //Do something}voidFUNCB () {Std::lock_guard<std::recursive_mutex>Lock(mutex); //Do something}voidFUNCC () {Std::lock_guard<std::recursive_mutex>Lock(mutex); Funca (); FUNCB ();}intMain () {//the same thread can get the same mutex multiple times without deadlockFUNCC (); return 0;}
It is important to note that recursive locks are used as few as possible for the following reasons:
Allowing recursive mutex to easily indulge the generation of complex logic, resulting in some multi-threaded synchronization caused by the obscure problem;
The efficiency of recursive lock is lower than that of non-recursive lock.
Although recursive locks can be obtained multiple times on the same thread, the number of fetches is too frequent to cause problems and std::system errors are raised.
4. Timeout lock
Std::time_mutex is an exclusive lock that is timed out, Std::recursive_timed_mutex is a recursive lock that is timed out, mainly used to increase the timeout wait function when acquiring a lock, to set a time to wait to acquire a lock, and to do other things after the timeout. The timeout lock has two more interfaces for acquiring locks, Try_lock_for and try_lock_until, which are used to obtain the time-out of the mutex.
The Try_lock_for function accepts a time range, indicating that the thread is blocked if it is not acquired (unlike Std::mutex's Try_lock () in the time range, and Try_lock returns False if the lock is not obtained when called). If the lock is freed by another thread during this time, the thread can obtain a lock on the mutex and return FALSE if it times out (that is, the lock was not acquired within the specified time).
The Try_lock_until function takes a point-in-time as an argument, and if the thread does not acquire a lock before the specified point in time is blocked, the thread can obtain a lock on the mutex if the lock is released by the other thread during the period, or if the lock is not acquired within the specified time, then returns False
#include <iostream>//Std::cout#include <chrono>//Std::chrono::milliseconds#include <thread>//Std::thread#include <mutex>//Std::timed_mutexStd::timed_mutex MTX;voidFireworks () {//waiting to get a Lock:each thread prints "-" every 200ms: while(!mtx.try_lock_for (Std::chrono::milliseconds ( $)) {std::cout<<"-"; } //got a lock!-wait for 1s, then this thread prints "*"Std::this_thread::sleep_for (Std::chrono::milliseconds ( +)); Std::cout<<"*\n"; Mtx.unlock ();}intMain () {Std::thread threads[Ten]; //Spawn Threads: for(inti =0; i<Ten; ++i) threads[i]=Std::thread (Fireworks); for(auto&th:threads) Th.join (); return 0;}
C11 Thread Management: Mutual exclusion lock