std::unique_lock în C++

Blocare de mutex flexibilă: poate fi eliberată și reluată, amânată, mutată; necesară pentru condition_variable.

Sintaxă
#include <mutex>
std::unique_lock<std::mutex> blocare(m);
std::unique_lock blocare(m, std::defer_lock);   // nu ocupă încă
void lock();  void unlock();  bool owns_lock() const noexcept;

Ce face std::unique_lock

std::unique_lock face tot ce face std::lock_guard (eliberează mutexul automat la final), dar are și metodele unlock() și lock(), ca să eliberezi mutexul mai devreme și să-l ocupi din nou.

Cu std::defer_lock se creează fără să ocupe mutexul; cu std::try_to_lock încearcă fără să aștepte. owns_lock() spune dacă deține mutexul acum. Poate fi mutat (returnat dintr-o funcție).

std::condition_variable::wait cere un std::unique_lock, pentru că trebuie să elibereze mutexul cât timp așteaptă.

Parametri

Parametru Descriere
m Mutexul gestionat.
std::defer_lock / std::try_to_lock Opțional: nu ocupa încă / încearcă fără să aștepți.

Ce returnează

Un obiect care gestionează mutexul.

Exemple

Exemplul 1: Eliberare înainte de o operație lentă

Mutexul protejează doar copierea datelor; procesarea lentă se face fără mutex, ca celelalte fire să nu aștepte degeaba.

#include <mutex>
#include <print>
#include <string>
#include <vector>

std::mutex m;
std::vector<std::string> comenzi{"pâine", "lapte", "ouă"};

int main() {
    std::unique_lock blocare(m);
    std::vector<std::string> deProcesat = comenzi;   // copie sub protecție
    comenzi.clear();
    blocare.unlock();                                // restul nu mai are nevoie de mutex

    std::println("deține mutexul: {}", blocare.owns_lock());
    for (const auto& c : deProcesat) {
        std::println("procesez {}", c);
    }

    blocare.lock();
    std::println("comenzi rămase: {}", comenzi.size());
}

Rezultat

deține mutexul: false
procesez pâine
procesez lapte
procesez ouă
comenzi rămase: 0

Exemplul 2: defer_lock și try_to_lock

#include <mutex>
#include <print>

int main() {
    std::mutex m;

    std::unique_lock a(m, std::defer_lock);
    std::println("a deține: {}", a.owns_lock());
    a.lock();
    std::println("a deține: {}", a.owns_lock());

    std::unique_lock b(m, std::try_to_lock);   // m este deja ocupat de a
    std::println("b a reușit: {}", b.owns_lock());
}

Rezultat

a deține: false
a deține: true
b a reușit: false

Greșeli frecvente

  • Apelezi unlock() pe un unique_lock care nu deține mutexul. Se aruncă std::system_error; verifică cu owns_lock().

Comenzi înrudite

  • std::lock_guard: Ocupă un mutex la creare și îl eliberează automat la ieșirea din bloc (RAII).
  • std::condition_variable: Permite unui fir să aștepte, fără să consume procesor, până când alt fir îl anunță că o condiție s-a îndeplinit.
  • std::mutex: Încuietoare pentru date comune: un singur fir de execuție o poate ține la un moment dat.
  • std::scoped_lock: Ocupă unul sau mai multe mutexuri deodată, fără risc de blocaj reciproc, și le eliberează automat.