|
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Member functions | ||||
Defined in header <mutex> | ||
template<class...MutexTypes> class scoped_lock; | (since C++17) | |
The classscoped_lock is a mutex wrapper that provides a convenientRAII-style mechanism for owning zero or more mutexes for the duration of a scoped block.
When ascoped_lock object is created, it attempts to take ownership of the mutexes it is given. When control leaves the scope in which thescoped_lock object was created, thescoped_lock is destructed and the mutexes are released. If several mutexes are given, deadlock avoidance algorithm is used as if bystd::lock.
Thescoped_lock class is non-copyable.
Contents |
| MutexTypes | - | the types of the mutexes to lock. The types must meet theLockable requirements unlesssizeof...(MutexTypes)==1, in which case the only type must meetBasicLockable |
| Member type | Definition |
mutex_type(conditionally present) | Ifsizeof...(MutexTypes)==1, member type |
constructs ascoped_lock, optionally locking the given mutexes(public member function)[edit] | |
destructs thescoped_lock object, unlocks the underlying mutexes(public member function)[edit] | |
operator= [deleted] | not copy-assignable (public member function)[edit] |
A common beginner error is to "forget" to give ascoped_lock variable a name, e.g.std::scoped_lock(mtx); (which default constructs ascoped_lock variable namedmtx) orstd::scoped_lock{mtx}; (which constructs a prvalue object that is immediately destroyed), thereby not actually constructing a lock that holds a mutex for the rest of the scope.
| Feature-test macro | Value | Std | Feature |
|---|---|---|---|
__cpp_lib_scoped_lock | 201703L | (C++17) | std::scoped_lock |
The following example usesstd::scoped_lock to lock pairs of mutexes without deadlock and is RAII-style.
#include <chrono>#include <functional>#include <iostream>#include <mutex>#include <string>#include <syncstream>#include <thread>#include <vector>usingnamespace std::chrono_literals; struct Employee{std::vector<std::string> lunch_partners;std::string id;std::mutex m; Employee(std::string id): id(id){}std::string partners()const{std::string ret="Employee "+ id+" has lunch partners: ";for(int count{};constauto& partner: lunch_partners) ret+=(count++?", ":"")+ partner;return ret;}}; void send_mail(Employee&, Employee&){// Simulate a time-consuming messaging operationstd::this_thread::sleep_for(1s);} void assign_lunch_partner(Employee& e1, Employee& e2){std::osyncstream synced_out(std::cout); synced_out<< e1.id<<" and "<< e2.id<<" are waiting for locks"<<std::endl; {// Use std::scoped_lock to acquire two locks without worrying about// other calls to assign_lunch_partner deadlocking us// 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_locks are needed, e.g. 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); synced_out<< 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 threads because mailing users about lunch assignments// takes a long timestd::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::osyncstream(std::cout)<< alice.partners()<<'\n'<< bob.partners()<<'\n'<< christina.partners()<<'\n'<< dave.partners()<<'\n';}
Possible output:
Alice and Bob are waiting for locksAlice and Bob got locksChristina and Bob are waiting for locksChristina and Alice are waiting for locksDave and Bob are waiting for locksDave and Bob got locksChristina and Alice got locksChristina and Bob got locksEmployee Alice has lunch partners: Bob, ChristinaEmployee Bob has lunch partners: Alice, Dave, ChristinaEmployee Christina has lunch partners: Alice, BobEmployee Dave has lunch partners: Bob
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| LWG 2981 | C++17 | redundant deduction guide fromscoped_lock<MutexTypes...> was provided | removed |
(C++11) | implements movable mutex ownership wrapper (class template)[edit] |
(C++11) | implements a strictly scope-based mutex ownership wrapper (class template)[edit] |