C++ Mutex

Da Andreabont's Wiki.

Compilazione

E' necessario, sotto Linux, eseguire il linking con la libreria pthread.

g++ <file.cpp> -std=c++11 -pthread

Mutex

Questo thread implementa il classico sistema di mutex, per permettere l'accesso ad una zona critica ad un solo thread per volta.

#include <mutex>

std::mutex mtx;           // Dichiaro il mutex (come variabile globale)

void function(int n) {

  mtx.lock(); // Inizio zona critica
  std::cout << n << std::endl;
  mtx.unlock(); // Fine zona critica

}

Lock guard

Questa classe permette di legare il mutex ad contesto corrente, liberando il lock all'uscita dal contesto. Il contesto stesso diventa la zona critica.

#include <mutex>

std::mutex mtx;

void function(int n) {

  std::lock_guard<std::mutex> lock(mtx); // Da questo momento siamo in zona critica

  ...

} // Qui si esce dalla zona critica

Unique lock

Questa classe permette di gestire più mutex in contemporanea, evitando così eventuali problemi di deadlock. Così come per lock_guard, la zona critica viene legata al contesto.

#include <mutex>

std::mutex mtx1;
std::mutex mtx2;

void function(int n) {

    std::unique_lock<std::mutex> lock1(mtx1, std::defer_lock);
    std::unique_lock<std::mutex> lock2(mtx2, std::defer_lock);

    std::lock(lock1, lock2);

} // Qui si esce dalla zona critica

Atomic

Con il template atomic è possibile proteggere dei tipi di dati dai problemi di accesso concorrente, rendendo le operazioni di lettura e scrittura atomiche.

#include <atomic>

std::atomic<int> value;

void more() {
   value++;
}

void less() {
   value--;
}

Condition variable

Questa classe permette di bloccare uno o più thread fino ad un segnale di sblocco, è utile per sincronizzare la partenza di più thread. Questa classe utilizza un unique_lock che lavora su un mutex. Il metodo wait può accettare una condizione se questa è falsa l'intera istruzione viene rivalutata in un loop. In questo modo è anche possibile implementare la logica dei semafori del C. Da notare che nel momento che il thread è in attesa la condition variable sblocca il mutex (in modo da liberare le risorse per gli altri) per poi ribloccarlo al termine della wait.

#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id (int id) {
  std::unique_lock<std::mutex> lck(mtx);
  cv.wait(lck, [](){return ready;});
  // ...
  std::cout << "thread " << id << '\n';
}

void go() {
  std::unique_lock<std::mutex> lck(mtx);
  ready = true;
  cv.notify_all();
}

int main ()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(print_id,i);

  std::cout << "10 threads ready to race...\n";
  go();                       // go!

  for (auto& th : threads) th.join();

  return 0;
}