Concurrencia de óxido

Concurrencia de óxido
La concurrencia se refiere a una característica que permite que partes independientes de un programa se ejecuten paralela a otras secciones del código. La concurrencia permite que diferentes partes de un programa se ejecuten simultáneamente, mejorando el rendimiento.

Pasemos por el bosque de la programación de concurrencia en el lenguaje de programación de óxido. Tenga en cuenta que este artículo no está diseñado para ser una guía completa para la programación de concurrencia. Solo sirve como base para expandir y crear aplicaciones más complejas.

Procesos e hilos

Cuando escribimos un programa normal y lo ejecutamos en un sistema de destino, el sistema operativo host ejecuta el código en un proceso. Un proceso se refiere a una unidad de un ejecutable especificado.

Sin embargo, en los sistemas y aplicaciones modernas, tiene partes del mismo código ejecutadas simultáneamente utilizando hilos.

En la mayoría de los casos, a menudo escuchará el término múltiple subproceso utilizado donde ocurre la concurrencia. Esto se debe a que esencialmente estamos generando múltiples hilos y permitiéndoles correr paralelos entre sí.

Tomemos un programa básico para ilustrar cómo funciona un programa normal y cómo usar la concurrencia para mejorarlo.

Considere un programa con dos bucles como se muestra:

usar std :: hilo;
use std :: tiempo :: duración;
fn main ()
para i en 0 ... = 5
println!("", i);
// dormir por 1000 ms
hilo :: dormir (duración :: from_millis (1000));

para i en 0 ... = 5
println!("", i);
hilo :: dormir (duración :: from_millis (1000));

En el código de ejemplo anterior, tenemos dos bucles que iteran de 0 a 5. Sin embargo, en cada iteración, dormimos durante 1000 milisegundos.

El método de hilo :: dormir nos permite poner un hilo específico para dormir durante la duración especificada.

Si ejecuta el código anterior, nota que el primer bucle espera que el segundo bucle se complete antes de que pueda comenzar a ejecutar.

Esto se debe a que ambos bucles están en un solo hilo.

Si queremos que ambos bucles funcionen simultáneamente, necesitamos ponerlos en diferentes hilos.

Óxido crea hilo

Podemos crear nuevos hilos usando el módulo de subprocesos. Es parte de la biblioteca estándar y nos proporciona un conjunto de herramientas y funciones para trabajar con hilos.

Podemos importarlo usando la declaración:

usar std :: hilo;

También necesitamos el módulo de duración del hilo de tiempo. Podemos importarlo como:

Use std :: tiempo :: duración

Para crear un nuevo hilo en Rust, use el método de hilo :: Spawn. Este método toma un cierre como argumento.

El cierre, en este caso, define que el código se ejecute dentro del hilo.

La sintaxis es como se muestra a continuación:

Thread :: Spawn (|| cierre)

Vamos a refinar el código anterior y colocemos cada construcción en un hilo separado. El código de ejemplo es como se muestra:

usar std :: hilo;
use std :: tiempo :: duración;
fn main ()
// Crear nuevo hilo
std :: thread :: spawn (moverse ||
para i en 0 ... = 5
println!("", i);
hilo :: dormir (duración :: from_millis (1000));

);
para i en 0 ... = 5
println!("", i);
hilo :: dormir (duración :: from_millis (1000));

En el programa de ejemplo anterior, creamos un nuevo hilo utilizando la función de hilo :: Spawn y pasamos el primer bucle como el cierre.

En el hilo principal, ejecutamos el segundo bucle. Esto permite que ambos bucles funcionen simultáneamente. El código anterior debe devolver la salida como:

0
0
1
1
2
2
3
3
4
4
5
5

¿Qué sucede si el hilo principal sale antes de que se complete el hilo "interno"?? Un ejemplo es como se muestra a continuación:

usar std :: hilo;
use std :: tiempo :: duración;
fn main ()
// hilo interior
std :: thread :: spawn (moverse ||
para i en 0 ... = 5
println!("", i);
hilo :: dormir (duración :: from_millis (1000));

);
// principal
para i en 0 ... = 5
println!("", i);
hilo :: dormir (duración :: from_millis (2));

En este ejemplo, el hilo principal lleva menos tiempo dormir y, por lo tanto, se completará más rápido antes de que se complete el hilo interno.

En tal caso, el hilo interno solo se ejecutará mientras el hilo principal se ejecuta. El código anterior devolverá la salida incompleta como:

0
0
1
2
3
4
5

Esto se debe a que el hilo "interno" termina antes de completar.

Manijas de unión de óxido

Hemos visto cómo se comporta un hilo si el hilo principal sale antes de que se complete. Podemos unir a las dos manijas para resolver tal caso y dejar que el otro hilo espere otro.

Unir las manijas permitirá que el hilo principal espere los otros hilos antes de la terminación.

Para unir las manijas, utilizamos el método de unión como se muestra en la sintaxis a continuación:

Let Handle_Name = Thread :: Spawn (cierre);
manejar_ nombre.unirse().desenvolver();

Vamos a redefinir nuestro ejemplo de bucle, donde el hilo principal sale temprano.

usar std :: hilo;
use std :: tiempo :: duración;
fn main ()
Let Handle = std :: thread :: spawn (moverse ||
para i en 0 ... = 5
println!("", i);
hilo :: dormir (duración :: from_millis (1000));

);
para i en 0 ... = 5
println!("", i);
hilo :: dormir (duración :: from_millis (2));

// Mango de unión
manejar.unirse().desenvolver();

En el código de ejemplo anterior, creamos una variable de identificación que contiene el hilo. Luego nos unimos al hilo utilizando el método Join ().

El método Unwrap nos permite manejar errores.

Dado que el hilo principal está durmiendo por un tiempo más corto, debe completarse antes del hilo "interno". Sin embargo, debe esperar a que el otro hilo salga debido al método de unión.

La salida es como se muestra:

0
0
1
2
3
4
5
1
2
3
4
5

Tenga en cuenta que el hilo principal genera todos los valores en una corta duración y espera a que el otro hilo se complete.

Cierre de movimiento de hilo de óxido

Es posible que haya notado la palabra clave Move dentro del cierre del hilo en nuestro ejemplo anterior. El cierre de movimiento se usa con el método de hilo :: Spawn para permitir compartir datos entre hilos.

Usando la palabra clave Move, podemos permitir que un hilo transfiera la propiedad de los valores a otro hilo.

Tome un programa de ejemplo a continuación:

usar std :: hilo;
use std :: tiempo :: duración;
fn main ()
Sea arr = [1,2,3,4,5];
Let Handle = std :: thread :: spawn (||
para yo en arr.iter ()
println!("", i);
hilo :: dormir (duración :: from_millis (100));

);
manejar.unirse().desenvolver();

En el código anterior, declaramos una matriz llamada ARR en el hilo principal. Luego generamos un nuevo hilo sin el cierre del movimiento.

Nota: Dado que estamos tratando de acceder a la matriz ARR y hacerla parte del entorno de cierre, la compilación fallará ya que no está disponible en ese hilo.

Podemos usar la palabra clave Move para forzar el cierre en el hilo para tomar posesión de la matriz.

Podemos arreglar el código anterior agregando el cierre de movimiento como se muestra:

usar std :: hilo;
use std :: tiempo :: duración;
fn main ()
Sea arr = [1,2,3,4,5];
Let Handle = std :: thread :: spawn (moverse ||
para yo en arr.iter ()
println!("", i);
hilo :: dormir (duración :: from_millis (100));

);
manejar.unirse().desenvolver();

Esto permite que el hilo tome posesión de la matriz e itera. Esto debería volver:

1
2
3
4
5

Conclusión

Esos fueron los fundamentos de la programación concurrente en el lenguaje de programación de óxido. Aunque este artículo sirve como base concreta para la concurrencia de óxido, no cubre conceptos avanzados. Puede consultar la documentación para más detalles.