En aquest tema, explorarem com gestionar la concurrència en Rust utilitzant fils. Els fils permeten executar múltiples tasques simultàniament, aprofitant millor els recursos del sistema i millorant el rendiment de les aplicacions.

Continguts

  1. Introducció als Fils
  2. Creació de Fils
  3. Comunicació entre Fils
  4. Sincronització de Fils
  5. Exercicis Pràctics

  1. Introducció als Fils

Els fils són una manera de dividir un programa en múltiples tasques que es poden executar simultàniament. En Rust, els fils són gestionats per la llibreria estàndard i proporcionen una manera segura i eficient de treballar amb concurrència.

Avantatges dels Fils

  • Millor Utilització del Processador: Permet executar múltiples tasques en paral·lel.
  • Rendiment Millorat: Pot reduir el temps d'execució de tasques complexes.
  • Reactivitat: Millora la capacitat de resposta de les aplicacions.

Desavantatges dels Fils

  • Complexitat: La gestió de fils pot ser complexa i propensa a errors.
  • Condicions de Cursa: Problemes que poden sorgir quan múltiples fils accedeixen a dades compartides simultàniament.

  1. Creació de Fils

En Rust, podem crear fils utilitzant la funció thread::spawn. Aquesta funció pren una clojure que conté el codi que volem executar en un nou fil.

Exemple Bàsic

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("Hola des del fil secundari: {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("Hola des del fil principal: {}", i);
        thread::sleep(Duration::from_millis(1));
    }

    handle.join().unwrap();
}

Explicació del Codi

  • thread::spawn: Crea un nou fil que executa la clojure passada com a argument.
  • thread::sleep: Pausa l'execució del fil durant el temps especificat.
  • handle.join(): Espera que el fil secundari acabi abans de continuar amb el fil principal.

  1. Comunicació entre Fils

Rust proporciona canals (channels) per a la comunicació segura entre fils. Els canals permeten enviar dades d'un fil a un altre.

Exemple de Canal

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("Hola");
        tx.send(val).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("He rebut: {}", received);
}

Explicació del Codi

  • mpsc::channel(): Crea un canal amb un transmissor (tx) i un receptor (rx).
  • tx.send(val): Envia un valor a través del canal.
  • rx.recv(): Rep un valor del canal.

  1. Sincronització de Fils

Per evitar condicions de cursa, Rust proporciona mecanismes de sincronització com Mutex i Arc.

Exemple amb Mutex i Arc

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!("Resultat: {}", *counter.lock().unwrap());
}

Explicació del Codi

  • Arc::new i Arc::clone: Permet compartir dades entre fils de manera segura.
  • Mutex::new i counter.lock(): Proporciona accés exclusiu a les dades compartides.

  1. Exercicis Pràctics

Exercici 1: Crear un Fil

Crea un programa que creï un fil que imprimeixi els números de l'1 al 5.

Exercici 2: Comunicació entre Fils

Crea un programa que utilitzi un canal per enviar un missatge d'un fil a un altre.

Exercici 3: Sincronització de Fils

Crea un programa que utilitzi Mutex i Arc per incrementar un comptador compartit entre múltiples fils.

Solucions

Solució Exercici 1

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..=5 {
            println!("Fil: {}", i);
        }
    });

    handle.join().unwrap();
}

Solució Exercici 2

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let msg = String::from("Hola des del fil");
        tx.send(msg).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Missatge rebut: {}", received);
}

Solució Exercici 3

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!("Comptador final: {}", *counter.lock().unwrap());
}

Conclusió

En aquest tema, hem après com crear i gestionar fils en Rust, com comunicar-nos entre fils utilitzant canals, i com sincronitzar fils per evitar condicions de cursa. Aquests conceptes són fonamentals per escriure programes concurrents i eficients en Rust. En el proper tema, explorarem el passatge de missatges com una altra tècnica per gestionar la concurrència.

© Copyright 2024. Tots els drets reservats