Mutex::lock în Rust

Obține accesul exclusiv la valoarea din Mutex, așteptând dacă alt fir o folosește; returnează un guard.

Sintaxă
fn lock(&self) -> LockResult<MutexGuard<'_, T>>
fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>>

Ce face Mutex::lock

lock() așteaptă până când niciun alt fir nu ține mutex-ul, apoi returnează un MutexGuard. Guard-ul se folosește ca o referință mutabilă (*guard += 1, guard.push(x)) și eliberează mutex-ul când dispare. Eliberarea mai devreme se face cu drop(guard).

try_lock() nu așteaptă: returnează Err(WouldBlock) dacă mutex-ul este ocupat. Un Err(Poisoned) de la oricare dintre ele înseamnă că un fir a intrat în panică ținând mutex-ul; datele sunt totuși accesibile cu into_inner() pe eroare.

Ce returnează

LockResult<MutexGuard<T>>, adică Result cu guard-ul sau cu eroarea de otrăvire.

Exemple

Exemplul 1: Guard-ul și eliberarea lui

use std::sync::Mutex;

fn main() {
    let sold = Mutex::new(100);

    let mut g = sold.lock().unwrap();
    *g -= 30;
    println!("în timp ce țin lock-ul, try_lock reușește: {}", sold.try_lock().is_ok());
    drop(g);

    println!("după drop, try_lock reușește: {}", sold.try_lock().is_ok());
    println!("sold: {} lei", sold.lock().unwrap());
}

Rezultat

în timp ce țin lock-ul, try_lock reușește: false
după drop, try_lock reușește: true
sold: 70 lei

Exemplul 2: Mutex otrăvit după o panică

Firul intră în panică în timp ce ține lock-ul. Hook-ul de panică este oprit, ca să nu apară mesajul cu numărul firului.

use std::panic;
use std::sync::Mutex;
use std::thread;

fn main() {
    panic::set_hook(Box::new(|_| {}));
    let date = Mutex::new(vec![1, 2, 3]);

    thread::scope(|s| {
        let r = s
            .spawn(|| {
                let mut g = date.lock().unwrap();
                g.push(4);
                panic!("eroare în timpul modificării");
            })
            .join();
        println!("firul a intrat în panică: {}", r.is_err());
    });

    match date.lock() {
        Ok(g) => println!("date: {:?}", *g),
        Err(otravit) => println!("mutex otrăvit, datele rămase: {:?}", *otravit.into_inner()),
    }
}

Rezultat

firul a intrat în panică: true
mutex otrăvit, datele rămase: [1, 2, 3, 4]

Greșeli frecvente

  • Scrii let x = m.lock().unwrap().clone(); și apoi te miri că modificările pe x nu ajung în mutex: ai lucrat pe o copie.
  • Faci match m.lock().unwrap().get(0) { ... } și în ramuri faci din nou lock(): guard-ul temporar trăiește până la sfârșitul lui match, deci firul se blochează.

Comenzi înrudite

  • Mutex: Protejează o valoare folosită din mai multe fire: lock() așteaptă până când firul curent este singurul cu acces.
  • RwLock: Lacăt cititori-scriitor: mai multe fire pot citi în același timp, dar scrierea are nevoie de acces exclusiv.
  • mem::drop: Distruge o valoare imediat, înainte de sfârșitul blocului: eliberează memoria, închide fișierul, deblochează mutex-ul.