std::lock (3) - Linux Manuals

std::lock: std::lock

NAME

std::lock - std::lock

Synopsis


Defined in header <mutex>
template< class Lockable1, class Lockable2, class... LockableN > (since C++11)
void lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );


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.

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.
// Run this code


  #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 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


try_lock attempts to obtain ownership of mutexes via repeated calls to try_lock
            (function template)
(C++11)


scoped_lock deadlock-avoiding RAII wrapper for multiple mutexes
            (class template)
(C++17)