thread::scope în Rust

Pornește fire care pot împrumuta variabile locale; scope așteaptă automat terminarea tuturor firelor create în el.

Sintaxă
pub fn scope<'env, F, T>(f: F) -> T
where F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T

s.spawn(|| ...)

Ce face thread::scope

thread::scope(|s| { ... }) creează o zonă în care pornești fire cu s.spawn(|| ...). Înainte ca scope să se termine, toate firele pornite în ea sunt așteptate automat. Datorită acestei garanții, firele pot împrumuta variabile locale (&date, chiar &mut), fără Arc și fără move.

s.spawn returnează un ScopedJoinHandle, pe care poți face join() ca să iei rezultatul. Dacă un fir intră în panică și nu i-ai făcut join, panica se propagă din scope. Valoarea returnată de closure-ul lui scope este returnată de scope.

Parametri

Parametru Descriere
f Closure-ul care primește s (obiectul Scope) și pornește firele.

Ce returnează

Valoarea returnată de closure.

Exemple

Exemplul 1: Fire care împrumută un vector local

Cele două jumătăți ale vectorului sunt adunate în paralel. Nu am avut nevoie de Arc sau de clone.

use std::thread;

fn main() {
    let vanzari = vec![120, 340, 90, 410, 250, 180];
    let (stanga, dreapta) = vanzari.split_at(vanzari.len() / 2);

    let (a, b) = thread::scope(|s| {
        let h1 = s.spawn(|| stanga.iter().sum::<i32>());
        let h2 = s.spawn(|| dreapta.iter().sum::<i32>());
        (h1.join().unwrap(), h2.join().unwrap())
    });

    println!("prima jumătate: {a} lei, a doua: {b} lei, total: {} lei", a + b);
    println!("vectorul este tot aici: {vanzari:?}");
}

Rezultat

prima jumătate: 550 lei, a doua: 840 lei, total: 1390 lei
vectorul este tot aici: [120, 340, 90, 410, 250, 180]

Exemplul 2: Modificare în paralel cu &mut

chunks_mut împarte vectorul în bucăți separate; fiecare fir primește &mut la bucata lui.

use std::thread;

fn main() {
    let mut preturi = vec![10.0, 20.0, 30.0, 40.0, 50.0, 60.0];

    thread::scope(|s| {
        for bucata in preturi.chunks_mut(2) {
            s.spawn(move || {
                for p in bucata.iter_mut() {
                    *p *= 1.19;
                }
            });
        }
    });

    let rotunjite: Vec<String> = preturi.iter().map(|p| format!("{p:.2}")).collect();
    println!("cu TVA: {}", rotunjite.join(", "));
}

Rezultat

cu TVA: 11.90, 23.80, 35.70, 47.60, 59.50, 71.40

Greșeli frecvente

  • Încerci să returnezi din scope o referință spre ceva creat în interiorul lui: datele trăiesc doar cât zona.
  • Folosești în fire același &mut de două ori: compilatorul refuză; împarte datele cu split_at_mut sau chunks_mut.

Comenzi înrudite

  • thread::spawn: Pornește un fir de execuție nou care rulează funcția dată; join() așteaptă terminarea lui și îi dă rezultatul.
  • Mutex: Protejează o valoare folosită din mai multe fire: lock() așteaptă până când firul curent este singurul cu acces.
  • AtomicUsize: Număr întreg care poate fi citit și modificat din mai multe fire fără Mutex, cu operații atomice.
  • Arc: Varianta lui Rc care poate fi împărțită între fire: contorul de proprietari este actualizat atomic.