0Pricing
Learn Rust Coding · Lección

Concurrencia sin miedo con hilos

Adéntrese en las primitivas de Rust para la programación concurrente, incluidos los hilos y el paso de mensajes, garantizando la ausencia de condiciones de carrera de datos.

Concurrencia sin miedo con hilos es una lección gratuita de Learn Rust Coding en CoddyKit. Esta es la lección 3 de 3. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Learn Rust Coding, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Learn Rust Coding incluye 3 lecciones en total.

Introducción a la concurrencia

¡Le damos la bienvenida a Concurrencia sin miedo con threads! En esta lección aprenderá a hacer que sus programas Rust realicen varias tareas a la vez.

La concurrencia consiste en ejecutar varios cálculos aparentemente al mismo tiempo. Es fundamental para crear aplicaciones con buena capacidad de respuesta y aprovechar los procesadores modernos de varios núcleos.

  • Los threads son unidades ligeras de ejecución dentro de un programa.
  • Cada thread puede ejecutar una parte distinta de su código.
  • El sistema de ownership de Rust ayuda a prevenir errores habituales de concurrencia.

Crear su primer thread

Rust proporciona std::thread::spawn para crear threads nuevos. Se le pasa un closure (una función anónima) que contiene el código que debe ejecutar el thread nuevo.

Pruebe a ejecutar este ejemplo para ver los threads en acción:

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

fn main() {
  println!("Hello from the main thread!");

  thread::spawn(|| {
    for i in 1..=5 {
      println!("Hi number {} from the spawned thread!", i);
      thread::sleep(Duration::from_millis(1));
    }
  });

  for i in 1..=3 {
    println!("Hi number {} from the main thread!", i);
    thread::sleep(Duration::from_millis(1));
  }
}

Esperar a los threads con `join`

En el ejemplo anterior, es posible que el thread creado no termine antes de que finalice el thread principal. Esto ocurre porque, de forma predeterminada, el thread principal no espera a los threads creados.

Para garantizar que un thread creado termine su trabajo, usamos el método join() en su JoinHandle. Esto bloquea el thread actual hasta que termina el thread unido.

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

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

  for i in 1..=3 {
    println!("Main: {}", i);
    thread::sleep(Duration::from_millis(1));
  }

  handle.join().unwrap(); // Wait for the spawned thread to finish
  println!("Spawned thread has finished!");
}

Mover datos a los threads

Cuando usa variables del entorno dentro de un closure de spawn, entran en juego las reglas de ownership de Rust. De forma predeterminada, los closures intentan tomar prestadas las variables.

Si el thread creado sobrevive al ámbito del thread principal en el que se definió la variable, esto puede provocar una referencia colgante. Para solucionarlo, use la palabra clave move antes de los parámetros del closure.

  • move obliga al closure a tomar el ownership de las variables capturadas.
  • Esto garantiza que los datos sean válidos durante toda la vida útil del thread nuevo.
use std::thread;

fn main() {
  let data = String::from("Hello from outer scope");

  let handle = thread::spawn(move || { // Use 'move' to take ownership of 'data'
    println!("Data in thread: {}", data);
  });

  handle.join().unwrap();
  // println!("Data after thread: {}", data); // This would cause a compile error!
  println!("Main thread finished.");
}

Comunicarse mediante paso de mensajes

Aunque compartir datos directamente entre threads (estado compartido) es posible en Rust, requiere una sincronización cuidadosa (por ejemplo, con Mutex y Arc, que se explican en otras lecciones).

Un enfoque más seguro y, a menudo, más sencillo para la concurrencia es el paso de mensajes. Los threads se comunican enviándose mensajes entre sí, lo que evita el acceso directo a la memoria compartida.

  • Un thread envía datos.
  • Otro thread recibe los datos.
  • La biblioteca estándar de Rust proporciona std::sync::mpsc para este fin.

Configurar un canal `mpsc`

std::sync::mpsc significa Multiple Producer, Single Consumer (múltiples productores, un solo consumidor). Esto significa que muchos hilos pueden enviar mensajes, pero solo un hilo puede recibirlos.

Para crear un canal, se llama a mpsc::channel(). Este devuelve una tupla que contiene:

  • Un Sender (tx): se utiliza para enviar mensajes.
  • Un Receiver (rx): se utiliza para recibir mensajes.

Veamos cómo crear uno y enviar un mensaje sencillo.

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

fn main() {
  // Create a new channel
  let (tx, rx) = mpsc::channel();

  thread::spawn(move || {
    let val = String::from("hi");
    tx.send(val).unwrap(); // Send the message
    // println!("val is {}", val); // Error: val moved to tx.send()
  });

  // Receive the message in the main thread
  let received = rx.recv().unwrap();
  println!("Got: {}", received);
}

Envío de varios mensajes

Puede enviar varios mensajes a través del mismo canal. Receiver tiene métodos para gestionarlos:

  • rx.recv(): bloquea el hilo actual hasta que se recibe un mensaje. Devuelve un Result.
  • rx.try_recv(): no bloquea. Devuelve un Result inmediatamente, ya sea con un mensaje o con un error si no hay ningún mensaje disponible.
  • También puede iterar directamente sobre Receiver para obtener mensajes hasta que el emisor cierre el canal.
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

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

  thread::spawn(move || {
    let msgs = vec!["hi", "from", "the", "thread"];
    for msg in msgs {
      tx.send(String::from(msg)).unwrap();
      thread::sleep(Duration::from_millis(10));
    }
  });

  // Iterate over the receiver to get all messages
  for received in rx {
    println!("Got: {}", received);
  }
  println!("All messages received!");
}

Múltiples productores, un solo consumidor

La parte «MP» de mpsc significa Multiple Producers (múltiples productores). Puede clonar un Sender para que varios hilos envíen mensajes al mismo Receiver.

Cada Sender clonado comparte el acceso al mismo canal, lo que permite enviar mensajes de forma concurrente desde distintos hilos.

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

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

  let tx1 = tx.clone(); // Clone the sender for another thread
  thread::spawn(move || {
    tx1.send(String::from("hello from tx1")).unwrap();
  });

  thread::spawn(move || {
    tx.send(String::from("hello from tx2")).unwrap();
  });

  // Collect all messages
  let mut received_messages: Vec<String> = vec![];
  for received in rx {
    received_messages.push(received);
    if received_messages.len() == 2 { break; } // Assuming 2 messages for this example
  }
  println!("Received: {:?}", received_messages);
}

Concurrencia sin temor con Rust

El sistema de ownership y de tipos de Rust es fundamental para lograr una concurrencia sin temor. Al hacer cumplir las reglas en tiempo de compilación, Rust evita errores habituales de concurrencia, como las carreras de datos.

  • El ownership evita que existan varias referencias mutables a los mismos datos.
  • La palabra clave move garantiza que los datos se transfieran de forma segura a un hilo nuevo.
  • El paso de mensajes (mpsc) evita por completo la memoria compartida, lo que hace que la comunicación sea segura por diseño.

Estos mecanismos le permiten escribir código concurrente con confianza, sabiendo que el compilador detectará muchos posibles problemas.

Comprobación de conceptos de concurrencia

¿Cuáles de las siguientes afirmaciones sobre las primitivas de concurrencia de Rust son VERDADERAS?

Repaso: hilos y paso de mensajes

¡Excelente trabajo! Ha aprendido los fundamentos de la concurrencia sin temor en Rust:

  • Creamos hilos nuevos mediante std::thread::spawn.
  • Usamos JoinHandle::join() para esperar a que un hilo termine.
  • La palabra clave move transfiere de forma segura el ownership de los datos a un hilo.
  • std::sync::mpsc proporciona un sistema sólido de paso de mensajes para la comunicación entre hilos.
  • Las reglas de ownership de Rust evitan las carreras de datos en tiempo de compilación, lo que hace más segura la programación concurrente.

Esta lección proporciona una base sólida para crear aplicaciones eficientes y con buena capacidad de respuesta. ¡Siga practicando!

Preguntas frecuentes

¿La lección «Concurrencia sin miedo con hilos» es gratis?

Sí — el texto completo de «Concurrencia sin miedo con hilos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Learn Rust Coding, actualiza a CoddyKit PRO. El curso de Learn Rust Coding incluye 3 lecciones en total.

¿Qué aprenderé en «Concurrencia sin miedo con hilos»?

Adéntrese en las primitivas de Rust para la programación concurrente, incluidos los hilos y el paso de mensajes, garantizando la ausencia de condiciones de carrera de datos. Practicas Learn Rust Coding con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Learn Rust Coding?

No se requiere experiencia previa. Learn Rust Coding en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 3.

¿Cuánto tiempo toma la lección «Concurrencia sin miedo con hilos»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Learn Rust Coding?

Sí. Cada lección de Learn Rust Coding incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Punteros inteligentes Box, Rc y Arc
  2. Mutabilidad interior: RefCell y Cell
  3. Concurrencia sin miedo con hilos
← Volver a Learn Rust Coding