lock_guard

lock_guard is the most basic mutex wrapper. It provides only two functions: a constructor and a destructor. When a lock_guard object is created, it takes ownership of the mutex that it receives. The mutex is locked until the lock_guard is destroyed.

#include <vector>
#include <iostream>

#include <thread>
#include <mutex>

::std::vector
   <
   unsigned
   >
   data;

::std::mutex
   mutex;

void load_data(unsigned value)
{
   //
   // Sleep briefly, then upload a value to 'data':
   //
   
   ::std::this_thread::sleep_for 
      (
      ::std::chrono::seconds(1)
      )
      ;
   
   //
   // Lock the mutex:
   //
   
   ::std::lock_guard
      <
      ::std::mutex
      >
      guard(mutex);
   
   data.push_back
      (
      value
      )
      ;
   
   //
   // The mutex is automatically unlocked here:
   //
   
   return;
}

int main(int argc, char ** argv)
{
   ::std::vector
      <
      ::std::thread
      >
      threads;
   
   //
   // Start 10 threads, each with a different value to
   // insert into 'data':
   //
   
   for(unsigned i = 0; i< 10; ++i)
   {
      threads.push_back
         (
         ::std::thread( load_data, i )
         )
         ;
   }
   
   for(unsigned i = 0; i< 10; ++i)
   {
      threads[i].join();
   }
   
   //
   // Display the contents of 'data':
   //
   
   for( auto const & datum : data )
   {
      ::std::cout << datum << " ";
   }
   ::std::cout << ::std::endl;
   
   return 0;
}

Possible output:

3 0 4 2 1 5 6 7 9 8

Note that, in general, the data was not inserted in the order 0 1 2 3 4 5 6 7 8 9. This is because the threads competed to acquire a lock_guard to the mutex, and the order in which the threads were successful is only known at runtime.

lock_guard objects can also adopt existing locked mutexes. In this case, the lock_guard is initialized with a reference to locked mutex. The mutex must be locked by the same thread that adopts it. To use this feature, add ::std::adopt_lock to the lock_guard constructor.

#include <mutex>

::std::mutex
   mutex0,
   mutex1;

int main(int argc, char ** argv)
{
   {
   
   mutex0.lock();
   
   //
   // Acquire the existing lock on mutex0:
   //
   
   ::std::lock_guard
      <
      ::std::mutex
      >
      guard
         (
         mutex0,
         ::std::adopt_lock
         )
         ;
   
   //
   // mutex0 is unlocked here:
   //
   
   }
   
   {
   
   //
   // Adopt 2 locks locked by ::std::lock (which uses a
   // deadlock avoidance algorithm to avoid deadlock):
   //
   
   ::std::lock(mutex0, mutex1);
   ::std::lock_guard
      <
      ::std::mutex
      >
      guard0
         (
         mutex0,
         ::std::adopt_lock
         )
         ;
   ::std::lock_guard
      <
      ::std::mutex
      >
      guard1
         (
         mutex1,
         ::std::adopt_lock
         )
         ;
   
   }
   
   return 0;
}