Comprenda el enfoque de Rust para la concurrencia que se basa en el concepto de "concurrencia intrépida".

La concurrencia es la capacidad de un programa para ejecutar múltiples tareas simultáneamente en el mismo núcleo de CPU. Las tareas simultáneas se ejecutan y completan en un tiempo superpuesto sin un orden específico, a diferencia del paralelismo, donde varias tareas o subtareas de la misma tarea se ejecutan al mismo tiempo en hardware con múltiples procesadores

Rust se destaca por sus características de desempeño y soporte para concurrencia de manera segura y eficiente. El enfoque de Rust para la concurrencia se basa en el concepto de "concurrencia intrépida", donde el lenguaje tiene como objetivo facilitar la escritura segura. código concurrente a través de su sistema de propiedad y préstamo que impone reglas estrictas en tiempo de compilación para evitar el seguimiento de datos y garantizar la memoria seguridad.

Comprender la concurrencia en Rust

Rust proporciona varias primitivas de concurrencia para escribir programas concurrentes, incluidos subprocesos, paso de mensajes, exclusión mutua, tipos atómicos y async/await para programación asíncrona.

instagram viewer

Aquí hay una descripción general de las primitivas de concurrencia de Rust:

  1. Hilos: Rust proporciona una estándar:: hilo módulo en su biblioteca estándar para crear y administrar hilos. Puede generar nuevos hilos con el hilo:: engendro función. El hilo:: engendro toma un cierre que contiene el código para su ejecución. También puede ejecutar subprocesos que pueden ejecutarse en paralelo, y Rust proporciona primitivas de sincronización para coordinar su ejecución. El verificador de préstamos asegura que las referencias no conduzcan a comportamientos inesperados.
  2. Paso de mensajes: El modelo de concurrencia de Rust admite el paso de mensajes entre subprocesos. Utilizará los canales implementados a través de la estándar:: sincronización:: mpsc módulo para el paso de mensajes. Un canal consta de un transmisor (Remitente) y un receptor (Receptor). Los subprocesos pueden enviar mensajes a través del transmisor y recibirlos a través del receptor. Esto proporciona una forma segura y sincronizada de comunicación entre subprocesos.
  3. Mutexes y tipos atómicos: Rust proporciona primitivas de sincronización, incluidos mutexes (estándar:: sincronización:: exclusión mutua) y tipos atómicos (estándar:: sincronización:: atómica), para garantizar el acceso exclusivo al intercambio de datos. Los mutexes permiten que varios subprocesos accedan a los datos al mismo tiempo y evitan carreras de datos. Los tipos atómicos proporcionan operaciones atómicas en datos compartidos, como incrementar un contador, sin necesidad de un bloqueo explícito.
  4. Asíncrono/Espera y Futuros: De óxido asíncrono/esperar La sintaxis proporciona funcionalidad para escribir código asíncrono que puede ejecutar simultáneamente. Los programas asincrónicos se ocupan de manera eficiente de las tareas vinculadas a E/S, lo que permite que los programas realicen otras tareas mientras esperan otras operaciones de E/S. de óxido asíncrono/esperar la sintaxis se basa en futuros, y puede impulsarlos con el asíncrono estándar o tokio bibliotecas de tiempo de ejecución.

Los subprocesos de Rust son livianos y la ausencia de sobrecarga de tiempo de ejecución los hace ideales para aplicaciones de alto rendimiento. Las primitivas de simultaneidad de Rust se integran a la perfección con múltiples bibliotecas y marcos para diferentes necesidades de simultaneidad.

Cómo usar hilos de generación en Rust

Usarás el estándar:: hilo módulo para generar hilos. El std:: subproceso:: engendro La función le permite crear un nuevo subproceso que se ejecutará simultáneamente con el subproceso principal o cualquier otro subproceso existente en su programa.

Así es como puede generar un hilo con el std:: subproceso:: engendro función:

usar estándar:: hilo;

fnprincipal() {
// generar un nuevo hilo
dejar thread_handle = thread:: spawn(|| {
// El código ejecutado en el nuevo hilo va aquí
imprimir!("¡Hola desde el nuevo hilo!");
});

// Espere a que termine el hilo generado
thread_handle.join().unwrap();

// El código ejecutado en el hilo principal continúa aquí
imprimir!("¡Hola desde el hilo principal!");
}

El principal función crea un nuevo hilo con el hilo:: engendro función pasando un cierre que contiene el código para la ejecución en el hilo (en este caso, el cierre es una función anónima). El cierre imprime un mensaje que indica que el nuevo hilo se está ejecutando.

El unirse método en el mango_hilo permite que el subproceso principal espere a que el subproceso generado complete la ejecución. Llamando unirse, la función garantiza que el subproceso principal espere a que se complete el subproceso generado antes de continuar.

Puede generar múltiples subprocesos y usar un bucle o cualquier otro Estructura de control de óxido para crear múltiples cierres y generar hilos para cada uno.

usar estándar:: hilo;

fnprincipal() {
dejar num_hilos = 5;

dejarmudo asas_hilo = vec![];

para i en0..num_hilos {
dejar thread_handle = hilo:: spawn(mover || {
imprimir!("Hola desde el hilo {}", i);
});
thread_handles.push (thread_handle);
}

para manejar en asas_de_hilo {
manejar.join().unwrap();
}

imprimir!("¡Todos los hilos terminaron!");
}

El ciclo for genera cinco subprocesos, cada uno asignado a un identificador único i con la variable de bucle. Los cierres capturan el valor de i con el mover palabra clave para evitar problemas de propiedad, y el asas_de_hilo vector almacena los hilos para más adelante en el unirse bucle.

Después de generar todos los subprocesos, el principal la función itera sobre el asas_de_hilo vectores, llamadas unirse en cada identificador y espera a que se ejecuten todos los subprocesos.

Pasar mensajes a través de canales

Puede pasar mensajes a través de hilos con canales. Rust proporciona funcionalidad para el paso de mensajes en el estándar:: sincronización:: mpsc módulo. Aquí, mpsc significa "productor múltiple, consumidor único" y permite la comunicación entre múltiples hilos enviando y recibiendo mensajes a través de canales.

Así es como implementa el paso de mensajes a través de canales de comunicación entre subprocesos en sus programas:

usar estándar:: sincronización:: mpsc;
usar estándar:: hilo;

fnprincipal() {
// Crear un canal
dejar (emisor, receptor) = mpsc:: canal();

// generar un hilo
hilo:: engendro(mover || {
// Enviar un mensaje a través del canal
remitente.enviar("¡Hola desde el hilo!").desenvolver();
});

// Recibir el mensaje en el hilo principal
dejar mensaje_recibido = receptor.recv().unwrap();
imprimir!("Mensaje recibido: {}", mensaje_recibido);
}

El principal función crea un canal con mpsc:: canal() que devuelve un remitente y un receptor. El remitente envía mensajes a la receptor que recibe los mensajes. El principal La función procede a generar subprocesos y mover la propiedad del Remitente al cierre del hilo. Dentro del cierre de rosca, el remitente.enviar() función envía un mensaje a través del canal.

El receptor.recv() La función recibe el mensaje deteniendo la ejecución hasta que el subproceso haya recibido el mensaje. El principal La función imprime el mensaje en la consola después de recibir un mensaje con éxito.

Tenga en cuenta que enviar un mensaje a través del canal consume al remitente. Si necesita enviar mensajes desde varios subprocesos, puede clonar el remitente con el remitente.clon() función.

Además, el mpsc El módulo proporciona otros métodos como probar_recv(), que sin bloqueo intenta recibir un mensaje, y iter(), que crea un iterador sobre los mensajes recibidos.

El paso de mensajes a través de canales proporciona una forma segura y conveniente de comunicarse entre subprocesos al tiempo que evita carreras de datos y garantiza una sincronización adecuada.

El modelo de propiedad y préstamo de Rust garantiza la seguridad de la memoria

Rust combina la propiedad, el préstamo y el verificador de préstamos para proporcionar un marco de programación sólido, seguro y concurrente.

El verificador de préstamo actúa como una red de seguridad, detectando problemas potenciales en tiempo de compilación en lugar de depender de verificaciones de tiempo de ejecución o recolección de elementos no utilizados.