En aquest tema, explorarem com gestionar l'estat compartit en programes concurrents en Rust. La concurrència és una característica poderosa que permet que múltiples parts d'un programa s'executin simultàniament, però també introdueix complexitat, especialment quan es tracta de compartir dades entre fils.

Objectius d'Aprenentatge

Al final d'aquest tema, hauràs après:

  • Com utilitzar Mutex per protegir l'accés a dades compartides.
  • Com utilitzar Arc per compartir dades entre fils de manera segura.
  • Com evitar condicions de carrera i bloquejos.

  1. Introducció a Mutex

Un Mutex (abreviatura de "mutual exclusion") és una estructura que garanteix que només un fil pugui accedir a una dada compartida en un moment donat. Això ajuda a evitar condicions de carrera, on múltiples fils accedeixen i modifiquen dades simultàniament de manera no segura.

Exemple Bàsic de Mutex

use std::sync::Mutex;

fn main() {
    let m = Mutex::new(5);

    {
        let mut num = m.lock().unwrap();
        *num = 6;
    }

    println!("m = {:?}", m);
}

Explicació:

  • Mutex::new(5) crea un nou Mutex que conté el valor 5.
  • m.lock().unwrap() bloqueja el Mutex per obtenir accés exclusiu a les dades. Si un altre fil ja ha bloquejat el Mutex, aquest fil esperarà fins que el Mutex estigui disponible.
  • *num = 6 modifica el valor protegit pel Mutex.
  • Quan num surt de l'àmbit, el Mutex es desbloqueja automàticament.

  1. Compartir Dades entre Fils amb Arc

Arc (abreviatura de "atomic reference counting") és una estructura que permet compartir dades entre múltiples fils de manera segura. Arc és similar a Rc, però és segur per a l'ús concurrent.

Exemple de Arc amb Mutex

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());
}

Explicació:

  • Arc::new(Mutex::new(0)) crea un Arc que conté un Mutex inicialitzat a 0.
  • Arc::clone(&counter) crea una nova referència al Arc original, permetent que múltiples fils comparteixin el mateix Mutex.
  • thread::spawn(move || { ... }) crea un nou fil que incrementa el valor protegit pel Mutex.
  • handle.join().unwrap() espera que cada fil acabi la seva execució.
  • Finalment, es mostra el valor del counter, que ha estat incrementat per cada fil.

  1. Evitar Condicions de Carrera i Bloquejos

Condicions de Carrera

Una condició de carrera ocorre quan múltiples fils accedeixen i modifiquen dades compartides simultàniament de manera no segura. L'ús de Mutex ajuda a evitar aquestes condicions, però és important assegurar-se que tots els accessos a les dades compartides estiguin protegits per un Mutex.

Bloquejos

Un bloqueig ocorre quan dos o més fils esperen indefinidament per recursos que estan bloquejats per altres fils. Per evitar bloquejos:

  • Mantingues el codi dins d'una secció crítica (el codi que està protegit per un Mutex) tan curt com sigui possible.
  • Evita bloquejar múltiples Mutex al mateix temps.

Exercici Pràctic

Exercici

Crea un programa que utilitzi Arc i Mutex per compartir un vector entre múltiples fils. Cada fil ha d'afegir un número al vector.

Solució

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let data = Arc::new(Mutex::new(vec![]));
    let mut handles = vec![];

    for i in 0..10 {
        let data = Arc::clone(&data);
        let handle = thread::spawn(move || {
            let mut vec = data.lock().unwrap();
            vec.push(i);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {:?}", *data.lock().unwrap());
}

Explicació:

  • Arc::new(Mutex::new(vec![])) crea un Arc que conté un Mutex inicialitzat amb un vector buit.
  • Cada fil afegeix un número al vector protegit pel Mutex.
  • Finalment, es mostra el contingut del vector.

Resum

En aquest tema, hem après com utilitzar Mutex per protegir l'accés a dades compartides i Arc per compartir dades entre fils de manera segura. També hem discutit com evitar condicions de carrera i bloquejos. Aquests conceptes són fonamentals per escriure programes concurrents segurs i eficients en Rust.

© Copyright 2024. Tots els drets reservats