Anuncios

Bienvenidos sean a este post, hoy hablaremos sobre otro metodo para los threads.

Anuncios
Anuncios

Cuando hablamos de los threads en este post mencionamos que uno de los inconvenientes que podemos tener es la race condition, de esto hablaremos mas adelante pero para evitarlo usamos un metodo llamado mutex, este metodo es una utilidad primitiva de exclusion mutua que nos permite proteger los datos compartidos, esto lo hace a traves del bloqueo de threads hasta que el lock se vuelve disponible, el mutex puede ser iniciado estaticamente o creado mediante el constructor new, a su vez cada mutex tiene un tipo que representa al tipo de dato que esta protegiendo, por ultimo los datos solo pueden ser accedidos a traves de los guardianes RAII que son devueltos tanto por lock como try_lock los cuales garantizan que los datos son siempre accedidos cuando el mutex esta bloqueado, para que mutex logre el bloqueo sobre un thread utiliza una estrategia llamada «envenamiento», este se considera envenenado cada vez que un thread entra en panico cuando mantiene un mutex, esto hace que los demas threads no puedan acceder mientras este contaminado, como dijimos este provee un resultado el cual indica si esta contaminado o no haciendo que se desenvuelvan estos resultado y propagando este panico para indicar que no se ve ninguna invariante invalida.

Anuncios

Aunque esto no impedira un acceso a todos los datos subyacentes, el tipo PoisonError tiene un metodo into_inner que devolvera al guardian que de otra forma se hubiera convertido en un bloque exitoso, esto nos permitira acceder a la informacion a pesar del bloqueo envenenado.

Anuncios

Pasemos a ver algunas de los metodos disponibles para mutex:

  • new, crea un nuevo mutex en un estado desbloqueado listo para usar
  • lock, adquiere un mutex y lo bloquea hasta que pueda trabajar
  • try_lock, intenta adquirir un bloqueo y si no se puede devuelve un error de lo contrario aplica al guardian
  • unlock, desactiva al guardian y desbloquea al mutex (altamente experimental)
  • is_poisoned, determina si el mutex esta envenenado o no
  • into_inner, consume al mutex y procede a devolver los datos subyacentes
  • get_mut, devuelve una referencia mutable a los datos subyacentes
Anuncios

Para entenderlo un poco mejor y verlo en accion vamos a realizar un nuevo ejemplo, para ello crearemos un nuevo proyecto que llamaremos mutez, una vez creado modificaremos el codigo generado en main.rs con el siguiente:

main.rs

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

fn main() 
{
	let mut mis_datos = vec![5, 8, 13];

	for i in 0..10
	{
		thread::spawn(move ||
		{
			mis_datos[0] += i;
		});
	}

	thread::sleep(Duration::from_millis(50));
}
Anuncios

Primero importaremos dos crate para poder usar a los threads y el Duration para nuestros threads, en el main definiremos una variable de tipo vector mutable llamada mis_datos, luego usaremos un bucle que vaya de 0 a 9 donde crearemos un nuevo thread y moveremos al thread principal el dato de la posicion 0 mas el valor actual de i, por ultimo por fuera de este bucle agregaremos un sleep para generar uaa pequeña demora de 50 ms, coompilemos y veamos su salida:

tinchicus@dbn001vrt:~/lenguajes/rust/mutez$ cargo run
   Compiling mutez v0.1.0 (/home/tinchicus/lenguajes/rust/mutez)
error[E0382]: use of moved value: `mis_datos`
  --> src/main.rs:13:17
   |
8  |     let mut mis_datos = vec![5, 8, 13];
   |         ------------- move occurs because `mis_datos` has type `Vec<i32>`, which does not implement the `Copy` trait
...
13 |         thread::spawn(move ||
   |                       ^^^^^^^ value moved into closure here, in previous iteration of loop
...
16 |             mis_datos[0] += i;
   |             --------- use occurs due to use in closure

For more information about this error, try `rustc --explain E0382`.
error: could not compile `mutez` due to previous error
tinchicus@dbn001vrt:~/lenguajes/rust/mutez$
Anuncios

Como podemos ver no se pudo compilar debido a que el vector no posee una implementacion del trait Copy, del cual hablamos en este post, y el problema es que el movimiento se realizo en una iteracion previa y despues intentamos usarlo cuando en realidad ya fue movido, para solucionarlo debemos hacer la siguiente modificacion en el codigo:

main.rs

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

fn main() 
{
	let primos = Arc::new(Mutex::new(vec![5, 8, 13]));

	for i in 0..10
	{
		let primos = primos.clone();
		thread::spawn(move ||
		{
			let mut mis_datos = primos.lock().unwrap();
			mis_datos[0] += i;
		});
	}

	thread::sleep(Duration::from_millis(50));
}
Anuncios
Anuncios

La primera modificacion que haremos sera importar a Arc y Mutex para su implementacion, lo siguiente sera crear una nueva variable que sera inmutable llamada primos, pero en esta ocasion usaremos a Arc, este creara un apuntador de referencia de thread seguro lo cual nos permitira compartir la propiedad del elemento en el heap de la memoria, dentro de este nuevo objeto crearemos un mutex para el vector que teniamos antes, nuestro siguiente paso sera dentro del bucle donde definiremos una nueva variable la cual sera un clon de la definida anteriormente, volvemos a usar a spawn y move donde ahora tendremos la definicion de la variable mutable mis_datos pero esta vez le asignaremos el clon anterior pero observen que le agregamos a lock, esto hara que solamente un thread pueda accederlo e impedira que cualquier otro lo haga hasta que sea liberado, el unwrap es para manejar algun posible error, en la siguiente linea incrementaremos el valor de la posicion 0 con el valor actual de i, por ultimo generamos una demora de 50 ms, ahora si lo compilamos debemos tener una nueva salida semejante a esta:

tinchicus@dbn001vrt:~/lenguajes/rust/mutez$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/mutez`
tinchicus@dbn001vrt:~/lenguajes/rust/mutez$
Anuncios
Anuncios

Como pueden ver ahora funciono perfectamente porque sabemos que el movimiento se hara una vez y siempre trabajara con el mismo thread y no generara ninguno nuevo por cada pasada del bucle, es un poco mas complejo pero tengan esto en cuenta porque cuando deban trabajar con varios threads para mejorar la peformance de nuestra aplicacion sera muy util y necesario para garantizar el correcto de los threads, aunque no olviden que un problema que podemos tener con el mutex es el deadlock, esto ocurre cuando dos locks se quedan esperando a que el otro termine para continuar generando un bucle sin fin de espera y «freezando» la aplicacion.

Anuncios

En resumen, hoy hemos visto a Mutex, que es, para que sirve, como nos ayuda, como trabaja, algunas metodos que posee para su implementacion, un ejemplo con un problema y como podemos solucionarlo por medio de mutex, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios
pp258

Donación

Es para mantenimento del sitio, gracias!

$1.50