std::scoped_lock în C++

Ocupă unul sau mai multe mutexuri deodată, fără risc de blocaj reciproc, și le eliberează automat.

Sintaxă
#include <mutex>
std::scoped_lock blocare(m1, m2, ...);

Ce face std::scoped_lock

std::scoped_lock este ca std::lock_guard, dar primește oricâte mutexuri. Le ocupă pe toate folosind un algoritm care evită blocajul reciproc (deadlock), indiferent în ce ordine le scrii, și le eliberează la ieșirea din bloc.

Exemplul clasic este transferul între două conturi: fiecare cont are mutexul lui, iar transferul trebuie să le țină pe amândouă. Dacă un fir transferă A→B și altul B→A, ocuparea manuală în ordine diferită poate bloca ambele fire pentru totdeauna.

Cu un singur mutex, std::scoped_lock se comportă exact ca std::lock_guard, așa că mulți îl folosesc peste tot.

Parametri

Parametru Descriere
m1, m2, ... Mutexurile ocupate împreună.

Ce returnează

Un obiect care ține toate mutexurile ocupate cât timp există.

Exemple

Exemplul 1: Transferuri în ambele sensuri

Două fire fac transferuri în sensuri opuse în același timp. Totalul de bani rămâne mereu același.

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

struct Cont {
    std::mutex m;
    int sold;
};

void transfera(Cont& din, Cont& in, int suma) {
    std::scoped_lock blocare(din.m, in.m);
    din.sold -= suma;
    in.sold += suma;
}

int main() {
    Cont ana{{}, 1000};
    Cont ion{{}, 1000};

    std::thread t1([&] { for (int i = 0; i < 1000; ++i) transfera(ana, ion, 1); });
    std::thread t2([&] { for (int i = 0; i < 500; ++i) transfera(ion, ana, 2); });
    t1.join();
    t2.join();

    std::println("Ana: {}, Ion: {}, total: {}", ana.sold, ion.sold, ana.sold + ion.sold);
}

Rezultat

Ana: 1000, Ion: 1000, total: 2000

Greșeli frecvente

  • Scrii std::scoped_lock blocare(); sau uiți să dai mutexurile. Nu se ocupă nimic. Scrie mereu mutexurile între paranteze.

Comenzi înrudite

  • std::lock_guard: Ocupă un mutex la creare și îl eliberează automat la ieșirea din bloc (RAII).
  • 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.