Biennvenidos sean a este post, hoy hablaremos sobre una forma muy particular de acceder a informacion en la memoria.
Cuando en este post hablamos de los canales y la propiedad (ownership) y a esta altura podemos decir que un canal no es otra cosa que una propiedad y transforma a la transferencia en algo que no puede ser accedida por otro, pero el lenguaje nos permite que varios threads puedan a acceder a una ubicacion de memoria pero para ello debemos usar a mutex, de la cual hablamos en este post.
Continuando con lo visto hasta ahora vamos a tomar el proyecto que vinimos utilizando llamado canal, si no lo tienen simplemente generen uno con ese nombre, y en este modificaremos el codigo de main.rs de la siguiente manera:
main.rs
use std::thread;
use std::sync::Mutex;
fn main()
{
let contador = Mutex::new(0);
let mut manejos = vec![];
for _ in 0..10
{
let manejo = thread::spawn(move || {
let mut num = contador.lock().unwrap();
*num += 1;
});
manejos.push(manejo);
}
for manejo in manejos
{
manejo.join().unwrap();
}
println!("Resultado: {}", *contador.lock().unwrap());
}
Primero importaremos las librerias necesarias, despues en el main definiremos un objeto de tipo Mutex llamado contador, lo siguiente sera definir un vector vacio llamado manejos, despues tendremos un bucle for que generara 10 threads, en este caso incrementaremos una variable siempre en el mismo thread por medio del lock, una vez finalizada la liberaremos para que otro thread las adquiera y en este caso pasaremos lo realizado en el thread a manejos, lo siguiente es un bucle donde usara a join para unir a todos los threads, por ultimo mostraremos el valor en contador, este se obtiene mediante la cantidad de veces que fue llamado lock para cada vez que se bloqueo al thread, compilemos y veamos su salida:
tinchicus@dbn001vrt:~/lenguajes/rust/canal$ cargo build
Compiling canal v0.1.0 (/home/tinchicus/lenguajes/rust/canal)
error[E0382]: use of moved value: `contador`
--> src/main.rs:11:30
|
6 | let contador = Mutex::new(0);
| -------- move occurs because `contador` has type `Mutex<i32>`, which does not implement the `Copy` trait
...
11 | let manejo = thread::spawn(move || {
| ^^^^^^^ value moved into closure here, in previous iteration of loop
12 | let mut num = contador.lock().unwrap();
| -------- use occurs due to use in closure
For more information about this error, try `rustc --explain E0382`.
error: could not compile `canal` due to previous error
tinchicus@dbn001vrt:~/lenguajes/rust/canal$
Como el tipo Mutex no es de tipo primitivo no implementa a Copy trait, por lo tanto al mover en la iteracion previa del bucle nos impide poder trabajar con el y esto impide que lo compilemos, para que este codigo funcione debemos hacer las siguientes modificaciones:
main.rs
use std::thread;
use std::sync::{Arc, Mutex};
fn main()
{
let contador = Arc::new(Mutex::new(0));
let mut manejos = vec![];
for _ in 0..10
{
let contador = Arc::clone(&contador);
let manejo = thread::spawn(move || {
let mut num = contador.lock().unwrap();
*num += 1;
});
manejos.push(manejo);
}
for manejo in manejos
{
manejo.join().unwrap();
}
println!("Resultado: {}", *contador.lock().unwrap());
}
En este caso implementamos al Arc, el cual es un apuntador seguro de conteo de referencia para threads, el cual nos permitira poder trabajar de una mejor forma con nuestro thread, porque la siguiente modificacion sera la asignacion de un Arc para nuestro Mutex en el objeto contador, lo siguiente sera en el bucle for donde crearemos un clon de contador mediante el metodo clone de Arc, el cual nos creara un clon del puntero que ya existe y esto nos permitira manipularlo de forma correcta, el resto sigue de la misma forma, compilemos y veamos su salida:
tinchicus@dbn001vrt:~/lenguajes/rust/canal$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/canal`
Resultado: 10
tinchicus@dbn001vrt:~/lenguajes/rust/canal$
Al igual que sucedio cuando hablamos de Mutex en este post, tambien para poder manipularlos siempre necesitaremos de Arc, para mantener automaticamente a un apuntador para el conteo de referencias y permitir que el compilador pueda manejar perfectamente la informacion.
En resumen, hoy hemos visto como es un estado compartido entre threads, como es, como se puede crear, y las herramientas necesarias para hacerlo, 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.


Donación
Es para mantenimento del sitio, gracias!
$1.50
