std::lock_guard în C++

Ocupă un mutex la creare și îl eliberează automat la ieșirea din bloc (RAII).

Sintaxă
#include <mutex>
std::lock_guard<std::mutex> blocare(m);
std::lock_guard blocare(m);   // C++17, tipul este dedus

Ce face std::lock_guard

std::lock_guard este cea mai simplă și sigură cale de a folosi un mutex. Constructorul apelează lock(), destructorul apelează unlock(). Mutexul este eliberat la ieșirea din bloc, oricum s-ar ieși: normal, cu return sau prin excepție.

Nu poate fi deblocat mai devreme și nu poate fi mutat. Dacă ai nevoie de asta (sau de std::condition_variable), folosește std::unique_lock. Pentru mai multe mutexuri deodată, std::scoped_lock.

Parametri

Parametru Descriere
m Mutexul ocupat pe durata blocului.

Ce returnează

Un obiect care ține mutexul ocupat cât timp există.

Exemple

Exemplul 1: Un cont bancar folosit de mai multe fire

#include <mutex>
#include <print>
#include <thread>
#include <vector>

class Cont {
public:
    void depune(int suma) {
        std::lock_guard blocare(m_);
        sold_ += suma;
    }
    bool retrage(int suma) {
        std::lock_guard blocare(m_);
        if (suma > sold_) return false;   // mutexul se eliberează și aici
        sold_ -= suma;
        return true;
    }
    int sold() {
        std::lock_guard blocare(m_);
        return sold_;
    }
private:
    std::mutex m_;
    int sold_ = 0;
};

int main() {
    Cont c;
    std::vector<std::thread> fire;
    for (int i = 0; i < 5; ++i) {
        fire.emplace_back([&c] {
            for (int j = 0; j < 1000; ++j) c.depune(2);
        });
    }
    for (auto& t : fire) t.join();

    std::println("Sold: {} lei", c.sold());
    std::println("Retragere 20000: {}", c.retrage(20000));
    std::println("Retragere 9000: {}, sold {}", c.retrage(9000), c.sold());
}

Rezultat

Sold: 10000 lei
Retragere 20000: false
Retragere 9000: true, sold 1000

Exemplul 2: Eliberare automată la excepție

#include <mutex>
#include <print>
#include <stdexcept>

std::mutex m;

void operatie(bool esueaza) {
    std::lock_guard blocare(m);
    if (esueaza) throw std::runtime_error("operație eșuată");
}

int main() {
    try {
        operatie(true);
    } catch (const std::exception& e) {
        std::println("Eroare: {}", e.what());
    }
    std::println("Mutexul este liber: {}", m.try_lock());
    m.unlock();
}

Rezultat

Eroare: operație eșuată
Mutexul este liber: true

Greșeli frecvente

  • Scrii std::lock_guard<std::mutex>(m); fără nume de variabilă. Se creează un obiect temporar care eliberează mutexul imediat, la finalul instrucțiunii; secțiunea critică nu este protejată. Dă-i mereu un nume.

Comenzi înrudite

  • std::mutex: Încuietoare pentru date comune: un singur fir de execuție o poate ține la un moment dat.
  • std::unique_lock: Blocare de mutex flexibilă: poate fi eliberată și reluată, amânată, mutată; necesară pentru condition_variable.
  • std::scoped_lock: Ocupă unul sau mai multe mutexuri deodată, fără risc de blocaj reciproc, și le eliberează automat.