The scoped lock class is a mutex wrapper that makes owning one or more mutexes for the length of a scoped block easier.
Scoped locks lock a mutex when they’re constructed and unlock it when they’re destructed. When the control flow departs a scope, the C++ rules ensure that objects local to the scope being vacated are properly destructed. This implies that utilizing a scoped lock rather than manually executing lock()
and unlock()
is a good approach. This makes it impossible to not unlock the mutex even accidentally. An example of this is when an exception is triggered between lock()
and unlock()
.
std::scoped_lock
combines RAII-style semantics for owning one or more mutexes with the lock avoidance methods used by std::lock
. It gives RAII-style semantics for owning one or more mutexes. Mutexes are released in the opposite order from which they were acquired when std::scoped lock
is destroyed.
{
std::scoped_lock lock{_mutex1,_mutex2};
//do something
}
#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 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;}{std::scoped_lock lock(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);}}int main(){Employee alice("alice"), bob("bob"), christina("christina"), dave("dave");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';}
Lines 9 to 21: We create a structure named Employee
that returns an employee’s output and their assigned lunch partners.
Lines 23 to 41: We create a function named assign_lunch_partner
where scoped_lock combines RAII-style semantics for owning two mutexes and locks them.
Lines 43 to 56: We create threads that combine the mutexes and assign lunch partners to an employee.