std::lock(3)
π std::lock β C++ Standard Library π
okay so bestie, welcome to std::lock(3) from the C++ Standard Library (the original header says C++ Standard Libary, typo and all β we love a chaotic icon π). This is the deadlock-avoiding multi-lock function, and we're keeping every single detail. No cap.
πΈ NAME πΈ
std::lock β std::lock. That's it, that's the name, main character energy π
β¨ Synopsis β¨
Defined in header <mutex>.
template< class Lockable1, class Lockable2, class... LockableN >
void lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );
(since C++11) β yes bestie, she's modern.
Locks the given Lockable objects lock1, lock2, ..., lockn using a deadlock avoidance algorithm to avoid deadlock.
The objects are locked by an unspecified series of calls to lock, try_lock, and unlock. If a call to lock or unlock results in an exception, unlock is called for any locked objects before rethrowing. That's the safety net, and it's lowkey important π«Ά
π Parameters π
lock1, lock2, ... , locknβ theLockableobjects to lock. yes, all of them, bring the whole squad π
π Return value π
(none) β literally nothing, she returns void energy β¨
π¦ Notes π¦
- Boost provides a version of this function that takes a sequence of
Lockableobjects defined by a pair of iterators. cute crossover, no cap. std::scoped_lockoffers a RAII wrapper for this function, and is generally preferred to a naked call tostd::lock. so if you're reaching for a nakedstd::lock, pause, bestie β usually you want the RAII wrapper π₯
π Example π
The following example uses std::lock to lock pairs of mutexes without deadlock. iconic behavior.
the original also has a // Run this code marker right here, so run it bestie π
#include <chrono>
#include <functional>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
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 (auto n{lunch_partners.size()}; const auto& partner : lunch_partners)
ret += partner + (--n ? ", " : "");
return ret;
}
};
void send_mail(Employee&, Employee&)
{
// Simulate a time-consuming messaging operation
std::this_thread::sleep_for(std::chrono::milliseconds(696));
}
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 acquire two locks without worrying about
// other calls to assign_lunch_partner deadlocking 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 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);
// Superior 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");
// Assign in parallel threads because mailing users about lunch assignments
// takes a long time
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 output π
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
Dave and Bob are waiting for locks
Dave and Bob got locks
Christina and Alice 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
π See also π
unique_lockβ implements movable mutex ownership wrapper. (C++11) (class template)try_lockβ attempts to obtain ownership of mutexes via repeated calls totry_lock. (C++11) (function template)scoped_lockβ deadlock-avoiding RAII wrapper for multiple mutexes. (C++17) (class template)
mwah π that's the whole page. source/version/date vibes: http://cppreference.com, 2024.06.10, std::lock(3) β bye bestie, go lock responsibly π«Άβ¨
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms