Consider the use of mutexes, the most basic code is:
1#include <iostream>2#include <thread>3#include <mutex>4 5 Std::mutex G_my_mutex;6 7Std::lock_guard<std::mutex>Make_lock () {8 returnStd::lock_guard<std::mutex>(G_my_mutex);9 }Ten One voidWorkOnResource1 () { A - for(inti =0; I <10000; ++i) - { theStd::lock_guard<std::mutex>LK (G_my_mutex); ---gi; - } - } + - voidWorkOnResource2 () { + A for(inti =0; I <10000; ++i) at { -Auto LK =Make_lock (); -++gi; - } - } - in intMain () { - to std::thread T1 (workOnResource1); + std::thread T2 (WORKONRESOURCE2); - the T1.join (); * T2.join (); $ Panax NotoginsengStd::cout <<"gi="<<gi; -}
This is common in many examples. Wait, why is the 8th line compiled? That's because you didn't compile under c++17. Std::lock_guard is forbidden to copy and move. c++17 granteed Copy ellision allows the 8th line to be compiled.
Comparing lines 24th and 15th, which is more concise?
Std::lock_guard can also be used like this:
void WorkOnResource1 () { for (int010000; + + i) { G_my_ Mutex. Lock (); Std::lock_guard<std::mutex> lk (G_my_mutex, std::adopt_lock); --gi; }}
This means that when the LK object is constructed, it does not call Mutex::lock (). Because the lock I held before has been locked once (G_my_mutex.lock ()). Adopt is the expression of this meaning, the meaning of English is adoption.
When LK is refactored, it does not change to call Mutex::unlock on G_my_mutex.
Now consider the use of Unique_lock: For more complex mutex operations, for example: Locking with timeout time.
#include <iostream>#include<thread>#include<mutex>//Std::mutex, Std::lock_guard#include <chrono>usingMs =std::chrono::milliseconds;using namespacestd;intGI =0; Std::timed_mutex G_my_mutex;std::lock_guard<std::timed_mutex>Make_lock () {returnStd::lock_guard<std::timed_mutex> (G_my_mutex);//must not be written in a branch}voidWorkOnResource1 () { for(inti =0; I <10000; ++i) {Auto LK=Make_lock (); ++gi; }}std::unique_lock<std::timed_mutex>Make_lock2 () {Std::unique_lock<std::timed_mutex>LK (G_my_mutex, std::d efer_lock); returnLk//intentionally branch writing}voidWorkOnResource2 () { for(inti =0; I <10000; ++i) {Auto LK=Make_lock2 (); while(Lk.try_lock_for (Ms ( -))==false) {Std::cout<<"lock fail. Reason timeout. Now retry ..."; } --gi; }}intMain () {Std::thread T1 (WorkOnResource1); Std::thread T2 (WORKONRESOURCE2); T1.join (); T2.join (); Std::cout<<"gi="<<gi;}
Unique_lock supports move semantics so that it can fly out of {}. Like a std::unique_ptr. Similarly, it is also raii, when destructors are called Mutex::unlock ().
std::d efer_lock is a global variable, and the type is std::d efer_lock_t. Obviously, it is used for the overload resolution of a function to choose a different function to perform. Defer_lock means, for the time being, no lock action is invoked for G_my_mutex.
To prove that Unique_lock can move, you can:
Std::unique_lock<std::timed_mutex> LK (G_my_mutex, std::d efer_lock); Std::unique_lock<std::timed _mutex> lk2 (Std::move (LK)); Lk2. Lock ();
Scoped_lock is a newly introduced c++17, which is particularly simple when dealing with multiple mutexes:
Reference: Http://en.cppreference.com/w/cpp/thread/scoped_lock
C + + Scoped_lock,unique_lock,lock_guard