Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/thread/lock"

From cppreference.com
< cpp‎ | thread
(I don't think its UB if the lock is a recursive_mutex?)
m (fmt, {{c]], http -> https, headers sorted, capitalized 1st letter)
Line 1: Line 1:
 
{{cpp/title|lock}}
 
{{cpp/title|lock}}
 
{{cpp/thread/navbar}}
 
{{cpp/thread/navbar}}
{{ddcl | header=mutex | since=c++11 |
+
{{ddcl|header=mutex|since=c++11|
 
template< class Lockable1, class Lockable2, class... LockableN >
 
template< class Lockable1, class Lockable2, class... LockableN >
 
void lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );
 
void lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );
 
}}
 
}}
  
Locks the given {{named req|Lockable}} objects {{tt|lock1}}, {{tt|lock2}}, {{tt|...}}, {{tt|lockn}} using a deadlock avoidance algorithm to avoid deadlock.
+
Locks the given {{named req|Lockable}} objects {{c|lock1}}, {{c|lock2}}, {{tt|...}}, {{c|lockn}} using a deadlock avoidance algorithm to avoid deadlock.
  
The objects are locked by an unspecified series of calls to {{tt|lock}}, {{tt|try_lock}}, and {{tt|unlock}}. If a call to {{tt|lock}} or {{tt|unlock}} results in an exception, {{tt|unlock}} is called for any locked objects before rethrowing.
+
The objects are locked by an unspecified series of calls to {{tt|lock}}, {{tt|try_lock}}, and {{tt|unlock}}. If a call to {{tt|lock}} or {{tt|unlock}} results in an exception, {{tt|unlock}} is called for any locked objects before rethrowing.
  
 
===Parameters===
 
===Parameters===
 
{{par begin}}
 
{{par begin}}
{{par | lock1, lock2, ... , lockn | the {{named req|Lockable}} objects to lock}}
+
{{par|lock1, lock2, ... , lockn|the {{named req|Lockable}} objects to lock}}
 
{{par end}}
 
{{par end}}
  
Line 19: Line 19:
  
 
===Notes===
 
===Notes===
[http://www.boost.org/doc/libs/release/doc/html/thread/synchronization.html#thread.synchronization.lock_functions.lock_range Boost provides a version of this function] that takes a sequence of {{named req|Lockable}} objects defined by a pair of iterators.
+
[https://www.boost.org/doc/libs/release/doc/html/thread/synchronization.html#thread.synchronization.lock_functions.lock_range Boost provides a version of this function] that takes a sequence of {{named req|Lockable}} objects defined by a pair of iterators.
  
 
{{ltt|cpp/thread/scoped_lock|std::scoped_lock}} offers a [[cpp/language/raii|RAII]] wrapper for this function, and is generally preferred to a naked call to {{tt|std::lock}}.
 
{{ltt|cpp/thread/scoped_lock|std::scoped_lock}} offers a [[cpp/language/raii|RAII]] wrapper for this function, and is generally preferred to a naked call to {{tt|std::lock}}.
  
 
===Example===
 
===Example===
{{example|The following example uses {{tt|std::lock}} to lock pairs of mutexes without deadlock.
+
{{example
| code=
+
|The following example uses {{tt|std::lock}} to lock pairs of mutexes without deadlock.
 +
|code=
 +
#include <chrono>
 +
#include <functional>
 +
#include <iostream>
 
#include <mutex>
 
#include <mutex>
 +
#include <string>
 
#include <thread>
 
#include <thread>
#include <iostream>
 
 
#include <vector>
 
#include <vector>
#include <functional>
 
#include <chrono>
 
#include <string>
 
 
   
 
   
struct Employee {
+
struct Employee
 +
{
 
     Employee(std::string id) : id(id) {}
 
     Employee(std::string id) : id(id) {}
 
     std::string id;
 
     std::string id;
Line 42: Line 44:
 
     {
 
     {
 
         std::string ret = "Employee " + id + " has lunch partners: ";
 
         std::string ret = "Employee " + id + " has lunch partners: ";
         for( const auto& partner : lunch_partners )
+
         for (const auto& partner : lunch_partners)
 
             ret += partner + " ";
 
             ret += partner + " ";
 
         return ret;
 
         return ret;
Line 50: Line 52:
 
void send_mail(Employee &, Employee &)
 
void send_mail(Employee &, Employee &)
 
{
 
{
     // simulate a time-consuming messaging operation
+
     // Simulate a time-consuming messaging operation
 
     std::this_thread::sleep_for(std::chrono::seconds(1));
 
     std::this_thread::sleep_for(std::chrono::seconds(1));
 
}
 
}
Line 62: Line 64:
 
     }
 
     }
  
     // use std::lock to acquire two locks without worrying about  
+
     // Use std::lock to acquire two locks without worrying about  
 
     // other calls to assign_lunch_partner deadlocking us
 
     // other calls to assign_lunch_partner deadlocking us
 
     {
 
     {
Line 68: Line 70:
 
         std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock);
 
         std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock);
 
         std::lock_guard<std::mutex> lk2(e2.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)
+
    // 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> lk1(e1.m, std::defer_lock);
//       std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock);
+
    // std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock);
//       std::lock(lk1, lk2);
+
    // std::lock(lk1, lk2);
// Superior solution available in C++17
+
    // Superior solution available in C++17
//       std::scoped_lock lk(e1.m, e2.m);
+
    // std::scoped_lock lk(e1.m, e2.m);
 
         {
 
         {
 
             std::lock_guard<std::mutex> lk(io_mutex);
 
             std::lock_guard<std::mutex> lk(io_mutex);
Line 89: Line 91:
 
     Employee alice("alice"), bob("bob"), christina("christina"), dave("dave");
 
     Employee alice("alice"), bob("bob"), christina("christina"), dave("dave");
 
   
 
   
     // assign in parallel threads because mailing users about lunch assignments
+
     // Assign in parallel threads because mailing users about lunch assignments
 
     // takes a long time
 
     // takes a long time
 
     std::vector<std::thread> threads;
 
     std::vector<std::thread> threads;
Line 97: Line 99:
 
     threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob));
 
     threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob));
 
   
 
   
     for (auto &thread : threads) thread.join();
+
     for (auto &thread : threads)
 +
        thread.join();
 
     std::cout << alice.output() << '\n'  << bob.output() << '\n'
 
     std::cout << alice.output() << '\n'  << bob.output() << '\n'
 
               << christina.output() << '\n' << dave.output() << '\n';
 
               << christina.output() << '\n' << dave.output() << '\n';
 
}
 
}
 
|p=true
 
|p=true
| output=
+
|output=
 
alice and bob are waiting for locks
 
alice and bob are waiting for locks
 
alice and bob got locks
 
alice and bob got locks
Line 119: Line 122:
 
===See also===
 
===See also===
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc inc | cpp/thread/dsc unique_lock}}
+
{{dsc inc|cpp/thread/dsc unique_lock}}
{{dsc inc | cpp/thread/dsc try_lock}}
+
{{dsc inc|cpp/thread/dsc try_lock}}
{{dsc inc | cpp/thread/dsc scoped_lock}}
+
{{dsc inc|cpp/thread/dsc scoped_lock}}
 
{{dsc end}}
 
{{dsc end}}
  
 
{{langlinks|de|es|fr|it|ja|pt|ru|zh}}
 
{{langlinks|de|es|fr|it|ja|pt|ru|zh}}

Revision as of 09:41, 15 September 2023

 
 
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
(C++11)
Generic lock management
lock
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
(C++11)
Safe Reclamation
(C++26)
Hazard Pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11)(deprecated in C++20)
(C++11)(deprecated in C++20)
Memory ordering
Free functions for atomic operations
Free functions for atomic flags
 
Defined in header <mutex>
template< class Lockable1, class Lockable2, class... LockableN >
void lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );
(since C++11)

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.

Contents

Parameters

lock1, lock2, ... , lockn - the Lockable objects to lock

Return value

(none)

Notes

Boost provides a version of this function that takes a sequence of Lockable objects defined by a pair of iterators.

std::scoped_lock offers a RAII wrapper for this function, and is generally preferred to a naked call to std::lock.

Example

The following example uses std::lock to lock pairs of mutexes without deadlock.

#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 (const auto& partner : lunch_partners)
            ret += partner + " ";
        return ret;
    }
};
 
void send_mail(Employee &, Employee &)
{
    // Simulate a time-consuming messaging 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 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
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

See also

implements movable mutex ownership wrapper
(class template) [edit]
(C++11)
attempts to obtain ownership of mutexes via repeated calls to try_lock
(function template) [edit]
deadlock-avoiding RAII wrapper for multiple mutexes
(class template) [edit]