Aplicaremos algunas funciones a nuestro Threadpool vamos a ir explicando uno por uno, al final tú deberías terminar con tu implementación de la misma manera.

A continuación necesitamos crear una función asociada nombrada new para ThreadPool. También sabemos que new debe tener un parámetro que pueda aceptar 4 como argumento y debe devolver una instancia ThreadPool Implementemos la función new más simple que tendrá esas características:

pub struct ThreadPool; impl ThreadPool { pub fn new(size: usize) -> ThreadPool { ThreadPool } }

Definiremos el método execute ThreadPool para tomar un cierre como parámetro. Vemos que podemos tomar como parámetros cierres con tres rasgos diferentes: Fn, FnMut, y FnOnce. Necesitamos decidir qué tipo de cierre usar aquí. Sabemos que terminaremos haciendo algo similar al método thread::spawn de la biblioteca estándar, por lo que podemos ver qué límites tiene la firma de thread::spawn en su parámetro.

pub fn spawn<F, T>(f: F) -> JoinHandle<T> where F: FnOnce() -> T + Send + 'static, T: Send + 'static

Cambiamos la definición de ThreadPoolmantener un vector de thread::JoinHandle<()>instancias, una vez que se recibe un tamaño válido, nuestro ThreadPool crea un nuevo vector que puede contener size cierto tamaño de elementos. Para otros fines donde no conoces el tamaño, puedes usar with_capacity que preasignar el tamaño dentro del vector.

pub struct ThreadPool { threads: Vec<thread::JoinHandle<()>>, } impl ThreadPool { pub fn new(size: usize) -> ThreadPool { assert!(size > 0); let mut threads = Vec::with_capacity(size); for _ in 0..size { } ThreadPool { threads } }

Cambiaremos el nombre del campo ThreadPool de threads a workers porque ahora contendrá instancias Worker en lugar de instancias JoinHandle<()>. Como no es necesario que mainsepa cómo funcionan los worker, en este caso serán privadas. Necesitamos que se comuniquen entre hilos, es por eso que aplicaremos channel en este ejemplo.

El ThreadPool creará un canal y se mantendrá en el lado emisor, cada Worker se aferrara al lado receptor, crearemos una nueva estructura llamada Job que contendrá los cierres que queremos enviar por el canal.

El método execute enviará el trabajo que desea ejecutar en el lado de envío del canal. En su hilo, el bucle Worker pasará por su lado receptor del canal y ejecutará los cierres de cualquier trabajo que reciba.

Pero aquí tendremos un problema, la implementación del canal que proporciona Rust es de múltiples productores, un solo consumidor. Esto significa que no podemos simplemente clonar el extremo consumidor del canal para arreglar este código.

Para solucionar la parte de compartir la propiedad entre múltiples subprocesos y permitir que los subprocesos muten el valor, debemos usar Arc<Mutex<T>>. El Arc permitirá que varios worker posean el receptor y Mutex garantizará que solo un worker obtenga un trabajo del receptor a la vez.

Finalmente implementamos el método execute. También cambiaremos Job de una estructura a un alias de tipo para un objeto trait que contenga el tipo de cierre que execute recibe.

Después de crear una nueva instancia Job con el cierre que ingresamos execute, enviamos ese trabajo al final del canal de envío. Estaremos haciendo un llamado unwrap en caso de que el envío fracase.

NOTA: No te preocupes si aún no te sientes temerario y arriesgado de crear tu solución, en recursos dejaré el archivo para que veas cómo se implementa.

Es hora de probar nuestro código, pero espera, habrás notado que en esta parte no te di código para seguir, pero si te di las indicaciones de lo que deberías hacer, como reto final intenta hacer la solución, todos los términos que te di los hemos visto anteriormente, así que esperemos que no tengas ningún problema. Al finalizar tu solución, intenta correr tu código y debe darte una salida como la que muestro en la imagen.

Un tip para tu proyecto, nuestra biblioteca de librerías en lib.rs debe lucir de esta manera:

use std::sync::mpsc; use std::sync::Arc; use std::sync::Mutex; use std::thread;

Aquí te dejo una referencia donde puedes recordar cuáles son todos los traitsque existen y puedes usar https://doc.rust-lang.org/rust-by-example/trait.html.

Por otro lado, como solo estoy llamando a Threadpool` en nuestro archivo main, pero esta es una librería que creamos, como tal aún no se encuentra dentro de la biblioteca de Rust, es por eso que debemos llamarlo de la siguiente manera:

extern crate nombredetuproyecto;