lock (C++)

Locks are a synchronization technique used to limit access to a resource in an environment where there are many threads of execution. A good example of a lock is a Mutex.

Resources

std::mutex foo,bar;
 
void task_a () {
  // foo.lock(); bar.lock(); // replaced by:
  std::lock (foo,bar);
  std::cout << "task a\n";
  foo.unlock();
  bar.unlock();
}
 
void task_b () {
  // bar.lock(); foo.lock(); // replaced by:
  std::lock (bar,foo);
  std::cout << "task b\n";
  bar.unlock();
  foo.unlock();
}
 
int main ()
{
  std::thread th1 (task_a);
  std::thread th2 (task_b);
 
  th1.join();
  th2.join();
 
  return 0;
}

Cool types of locks

shared_lock

shared_lock allows deferred locking, timed locking and transfer of lock ownership.

https://en.cppreference.com/w/cpp/thread/shared_lock/shared_lock

#include <shared_mutex>
#include <syncstream>
#include <iostream>
#include <thread>
#include <chrono>
 
std::shared_timed_mutex m;
int i = 10;
 
void read_shared_var(int id)
{
   // both the threads get access to the integer i
   std::shared_lock<std::shared_timed_mutex> slk(m);
   const int ii = i; // reads global i
 
   std::osyncstream(std::cout) << "#" << id << " read i as " << ii << "...\n";
   std::this_thread::sleep_for(std::chrono::milliseconds(10));
   std::osyncstream(std::cout) << "#" << id << " woke up..." << std::endl;
}
 
int main()
{
   std::thread r1 {read_shared_var, 1};
   std::thread r2 {read_shared_var, 2};
 
   r1.join();
   r2.join();
}

unique_lock

unique_lock locks in exclusive mode.

https://en.cppreference.com/w/cpp/thread/unique_lock

https://stackoverflow.com/questions/14709233/how-to-use-create-unique-lock-in-c

#include <mutex>
#include <thread>
#include <iostream>
 
struct Box
{
    explicit Box(int num) : num_things{num} {}
 
    int num_things;
    std::mutex m;
};
 
void transfer(Box &from, Box &to, int num)
{
    // don't actually take the locks yet
    std::unique_lock lock1{from.m, std::defer_lock};
    std::unique_lock lock2{to.m, std::defer_lock};
 
    // lock both unique_locks without deadlock
    std::lock(lock1, lock2);
 
    from.num_things -= num;
    to.num_things += num;
 
    // 'from.m' and 'to.m' mutexes unlocked in 'unique_lock' dtors
}
 
int main()
{
    Box acc1{100};
    Box acc2{50};
 
    std::thread t1{transfer, std::ref(acc1), std::ref(acc2), 10};
    std::thread t2{transfer, std::ref(acc2), std::ref(acc1), 5};
 
    t1.join();
    t2.join();
 
    std::cout << "acc1: " << acc1.num_things << "\n"
                 "acc2: " << acc2.num_things << '\n';
}