lock_guard
Class lock_guard is a mutex wrapper that provides a convenient RAII style mechanism for occupying mutexes during scope blocks.
Create lock_ When the guard object, it attempts to receive ownership of the given mutex. Control leave create lock_ Destroy lock when the scope of the guard object_ Guard and release mutex.
lock_ The guard class cannot be copied.
Member type mutex_type Mutex Member function Member function (Constructor) structure lock_guard ,Optionally lock the given mutex(Public member function) (Destructor) Deconstruction lock_guard Object to unlock the underlying mutex(Public member function) operator=[Deleted] Non reproducible assignment(Public member function)
#include <thread> #include <mutex> #include <iostream> int g_i = 0; std::mutex g_i_mutex; // Protection g_i void safe_increment() { std::lock_guard<std::mutex> lock(g_i_mutex); ++g_i; std::cout << std::this_thread::get_id() << ": " << g_i << '\n'; // g_i_mutex is automatically released when the lock leaves the scope } int main() { std::cout << "main: " << g_i << '\n'; std::thread t1(safe_increment); std::thread t2(safe_increment); t1.join(); t2.join(); std::cout << "main: " << g_i << '\n'; }
Possible outputs: main: 0 140641306900224: 1 140641298507520: 2 main: 2
scoped_lock
Class scoped_lock is a mutex wrapper that provides a convenient RAII style mechanism. It occupies one or more mutexes during the existence of scope blocks.
Create scoped_lock object, which attempts to take ownership of the given mutex. Control the creation of scoped_ Destruct scoped when the scope of lock object_ Lock and release mutex. If several mutexes are given, the deadlock free algorithm is used, as with std::lock.
scoped_ The lock class is not replicable.
Member type mutex_type (if sizeof...(MutexTypes)==1) Mutex , MutexTypes... Individual types in Member function (Constructor) structure scoped_lock ,Optionally lock the given mutex(Public member function) (Destructor) Deconstruction scoped_lock Object to unlock the underlying mutex(Public member function) operator=[Deleted] Not replicable(Public member function)
#include <mutex> #include <thread> #include <iostream> #include <vector> #include <functional> #include <chrono> #include <string> struct Employee { Employee(std::string id) : id(id) {} std::string id; std::vector<std::string> lunch_partners; std::mutex m; std::string output() const { std::string ret = "Employee " + id + " has lunch partners: "; for (const auto &partner : lunch_partners) ret += partner + " "; return ret; } }; void send_mail(Employee &, Employee &) { // Simulate time-consuming sending operation std::this_thread::sleep_for(std::chrono::seconds(1)); } void assign_lunch_partner(Employee &e1, Employee &e2) { static std::mutex io_mutex; { std::lock_guard<std::mutex> lk(io_mutex); std::cout << e1.id << " and " << e2.id << " are waiting for locks" << std::endl; } { // Using std::scoped_lock obtains two locks without worrying // Other assignment_ lunch_ Partner call deadlock // And it also provides a convenient RAII style mechanism std::scoped_lock lock(e1.m, e2.m); // Equivalent code 1 (using std::lock and std::lock_guard) // std::lock(e1.m, e2.m); // std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock); // std::lock_guard<std::mutex> lk2(e2.m, std::adopt_lock); // Equivalent code 2 (if unique_lock is required, for example, for condition variables) // std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock); // std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock); // std::lock(lk1, lk2); { std::lock_guard<std::mutex> lk(io_mutex); std::cout << e1.id << " and " << e2.id << " got locks" << std::endl; } e1.lunch_partners.push_back(e2.id); e2.lunch_partners.push_back(e1.id); } send_mail(e1, e2); send_mail(e2, e1); } int main() { Employee alice("alice"), bob("bob"), christina("christina"), dave("dave"); // Assign in parallel thread, because it takes a long time to send email on lunch assignment std::vector<std::thread> threads; threads.emplace_back(assign_lunch_partner, std::ref(alice), std::ref(bob)); threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(bob)); threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(alice)); threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob)); for (auto &thread : threads) thread.join(); std::cout << alice.output() << '\n' << bob.output() << '\n' << christina.output() << '\n' << dave.output() << '\n'; }
Possible outputs:
alice and bob are waiting for locks alice and bob got locks christina and bob are waiting for locks christina and alice are waiting for locks dave and bob are waiting for locks dave and bob got locks christina and alice got locks christina and bob got locks Employee alice has lunch partners: bob christina Employee bob has lunch partners: alice dave christina Employee christina has lunch partners: alice bob Employee dave has lunch partners: bob
unique_lock
Class unique_lock is a universal mutex wrapper that allows delayed locking, limited attempts to lock, recursive locking, ownership transfer, and use with conditional variables.
Class unique_lock is movable, but not replicable - it satisfies moveconstructible and moveassignable, but not copyconstructable or copyassignable.
Class unique_lock meets the basic lockable requirements. Unique if Mutex meets the lockable requirement_ Lock also meets the lockable requirements (for example, it can be used for std::lock); If Mutex meets the timedlockable requirement, then unique_lock also meets the requirements of timedlockable.
Member type mutex_type Mutex Member function (Constructor) structure unique_lock ,Optionally lock the mutex provided(Public member function) (Destructor) If the association is mutually exclusive, the possession is unlocked(Public member function) operator= If it is possessed, it unlocks the mutual exclusion and obtains the ownership of the other party(Public member function) locking lock Lock Association mutex(Public member function) try_lock Try to lock the associated mutex, and return if mutex is unavailable(Public member function) try_lock_for An attempt was made to lock the associated timed lock (TimedLockable) Mutex. If mutex is not available for a given duration, return(Public member function) try_lock_until Attempting to lock an association can be timed to lock (TimedLockable) Mutex, if mutex is still unavailable at the specified time point, return(Public member function) unlock Unlock Association mutex(Public member function) Modifier swap With another std::unique_lock Exchange status(Public member function) release Disassociate the mutex without unlocking it(Public member function) Observer mutex Returns a pointer to the associated mutex(Public member function) owns_lock Test whether the lock holds its associated mutex(Public member function) operator bool Test whether the lock holds its associated mutex(Public member function)Nonmember function std::swap(std::unique_lock) (C++11) std::swap yes unique_lock Specialization of(Function template)
#include <mutex> #include <thread> #include <chrono> struct Box { explicit Box(int num) : num_things{num} {} int num_things; std::mutex m; }; void transfer(Box &from, Box &to, int num) { // The lock has not been actually removed std::unique_lock<std::mutex> lock1(from.m, std::defer_lock); std::unique_lock<std::mutex> lock2(to.m, std::defer_lock); // Lock two unique_lock without dead lock std::lock(lock1, lock2); from.num_things -= num; to.num_things += num; // 'from.m 'and' to M 'mutual exclusion unlocked in' unique '_ Lock 'destructor } int main() { Box acc1(100); Box acc2(50); std::thread t1(transfer, std::ref(acc1), std::ref(acc2), 10); std::thread t2(transfer, std::ref(acc2), std::ref(acc1), 5); t1.join(); t2.join(); }
shared_lock
Class shared_lock is a universal shared and mutually exclusive ownership wrapper, which allows delayed locking, timed locking and the transfer of lock ownership. Lock shared_lock, which locks the associated shared mutex in shared mode (std::unique_lock can be used to lock in exclusive mode).
shared_lock class is movable, but not replicable -- it meets the requirements of moveconstructible and moveassignable, but not copyconstructable or copyassignable.
shared_lock meets lockable requirements. If Mutex meets the sharedtimedlockable requirements, shared_lock also meets the requirements of timedlockable.
To wait for shared mutex in shared ownership mode, use std::condition_variable_any (std::condition_variable requires std::unique_lock, so it can only wait in the unique ownership mode).
Member type mutex_type Mutex Member function (Constructor) structure shared_lock ,Optionally lock the mutex provided(Public member function) (Destructor) Unlock associated mutex(Public member function) operator= If you own it, you can unlock the mutual exclusion and then get the ownership of the other party(Public member function) Share lock lock Lock associated mutex(Public member function) try_lock Attempt to lock the associated mutex(Public member function) try_lock_for Attempt to lock the associated mutex to specify the duration(Public member function) try_lock_until Attempts to lock the associated mutex until the specified point in time(Public member function) unlock Unlock mutually exclusive Association(Public member function) Modifier swap With another shared_lock Exchange data members(Public member function) release Disassociate mutex Without unlocking(Public member function) Observer mutex Returns a pointer to the associated mutex(Public member function) owns_lock Test whether the lock occupies its associated mutex(Public member function) operator bool Test whether the lock occupies its associated mutex(Public member function) Nonmember function std::swap(std::shared_lock)(C++14) std::swap yes shared_lock Specialization of(Function template)
lock
Lock a given lockable object lock1, lock2,..., lockn, and use deadlock free algorithm to avoid deadlock.
To lock and try_ Unspecified series of calls to lock and unlock lock the object. If calling lock or unlock causes an exception, unlock is called on any locked object before re throwing.
#include <mutex> #include <thread> #include <iostream> #include <vector> #include <functional> #include <chrono> #include <string> struct Employee { Employee(std::string id) : id(id) {} std::string id; std::vector<std::string> lunch_partners; std::mutex m; std::string output() const { std::string ret = "Employee " + id + " has lunch partners: "; for( const auto& partner : lunch_partners ) ret += partner + " "; return ret; } }; void send_mail(Employee &, Employee &) { // Simulate time-consuming sending operation std::this_thread::sleep_for(std::chrono::seconds(1)); } void assign_lunch_partner(Employee &e1, Employee &e2) { static std::mutex io_mutex; { std::lock_guard<std::mutex> lk(io_mutex); std::cout << e1.id << " and " << e2.id << " are waiting for locks" << std::endl; } // Use std::lock to obtain two locks without worrying about assigning_ lunch_ Other calls from partners will deadlock us { std::lock(e1.m, e2.m); std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock); std::lock_guard<std::mutex> lk2(e2.m, std::adopt_lock); // Equivalent code (if unique_locks is required, for example, for condition variables) // std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock); // std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock); // std::lock(lk1, lk2); // Better solution available in C++17 // std::scoped_lock lk(e1.m, e2.m); { std::lock_guard<std::mutex> lk(io_mutex); std::cout << e1.id << " and " << e2.id << " got locks" << std::endl; } e1.lunch_partners.push_back(e2.id); e2.lunch_partners.push_back(e1.id); } send_mail(e1, e2); send_mail(e2, e1); } int main() { Employee alice("alice"), bob("bob"), christina("christina"), dave("dave"); // In parallel thread assignment, it takes a long time to send an email to the user to inform the lunch assignment std::vector<std::thread> threads; threads.emplace_back(assign_lunch_partner, std::ref(alice), std::ref(bob)); threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(bob)); threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(alice)); threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob)); for (auto &thread : threads) thread.join(); std::cout << alice.output() << '\n' << bob.output() << '\n' << christina.output() << '\n' << dave.output() << '\n'; }
Possible outputs:
alice and bob are waiting for locks alice and bob got locks christina and bob are waiting for locks christina and bob got locks christina and alice are waiting for locks christina and alice got locks dave and bob are waiting for locks dave and bob got locks Employee alice has lunch partners: bob christina Employee bob has lunch partners: alice christina dave Employee christina has lunch partners: bob alice Employee dave has lunch partners: bob
std::defer_lock, std::try_to_lock, std::adopt_lock
-
std::lock_ STD:: adopt of guard_ Lock parameter
- Join adopt_ After lock, call lock_ When using the constructor of guard, lock() is no longer required;
- adopt_guard is a structure object and serves as a marker to indicate that the mutex has been locked () and does not need to be locked ().
-
unique_ The second parameter of lock std::adopt_lock:
- Indicates that the mutex has been locked (), that is, it is not necessary to lock the mutex in the constructor.
- Premise: lock in advance
-
std::try_to_lock:
- Try to lock the mutex with mutex's lock(), but if the lock is not successful, it will return immediately and will not block there;
- Use try_ to_ The reason for lock is to prevent other threads from locking mutex for too long, which causes this thread to block in lock all the time
- Premise: cannot lock() in advance;
- owns_ The locks () method determines whether to get the lock. If so, it returns true
-
std::defer_lock:
- If there is no second parameter, lock mutex and add defer_lock starts a mutex without a lock
- The purpose of not locking it is to call unique later_ Some methods of lock
- Premise: cannot lock in advance
call_once
Function template. The first parameter of the function is a tag, and the second parameter is a function name.
Function: it can ensure that the function is called only once. It has the ability of mutex, and consumes less resources and is more efficient than mutex.
call_once() needs to be used in conjunction with a tag called std::once_flag; Actually once_flag is a structure called call_once() determines whether the function is executed through the tag. After the call is successful, the tag is set to a called state.
#include <iostream> #include <thread> #include <mutex> std::once_flag flag1, flag2; void simple_do_once() { std::call_once(flag1, []() { std::cout << "Simple example: called once\n"; }); } void may_throw_function(bool do_throw) { if (do_throw) { std::cout << "throw: call_once will retry\n"; // throw std::exception(); } std::cout << "Didn't throw, call_once will not attempt again\n"; } void do_once(bool do_throw) { try { std::call_once(flag2, may_throw_function, do_throw); } catch (...) { } } int main() { std::thread st1(simple_do_once); std::thread st2(simple_do_once); std::thread st3(simple_do_once); st1.join(); st2.join(); st3.join(); std::thread t1(do_once, true); std::thread t2(do_once, false); std::thread t3(do_once, true); std::thread t4(do_once, true); t1.join(); t2.join(); t3.join(); t4.join(); }
It may be:
Simple example: called once throw: call_once will retry Didn't throw, call_once will not attempt again