
- Fork-Join → Nos sirve para realizar cómputos exigentes sobre distintos hilos, de esta forma podemos realizar distintos productos matriciales sobre distintos hilos de ejecución al dividir en sub-tareas los productos fila x columna.
- Programación asíncrona → Permite que estos llamados a diferentes APIs sean tratados como tareas a ejecutar de forma concurrente, una vez obtenidos sus resultados los podremos combinar.
- Canales → Tienes una fuente masiva de datos (el sitio web escribiendo logs) y necesitas procesarlos sin bloquear el servidor. Un modelo de canales permite que el servidor "envíe" el log a un canal y continúe su trabajo, mientras uno o varios hilos trabajadores (workers) consumen esos mensajes del canal y los procesan de forma independiente.
- Actores → Cada documento puede ser un actor al igual que cada usuario. Los cambios de los usuarios son mensajes que llegan al mailbox del actor. El actor los procesa de forma secuencial y atómica, modificando su estado (contenido) en consecuencia.

Se observa que se lanza un hilo hijo sobre el hilo principal con una idea productor-consumidor. Mientras el hilo hijo agrega elementos al buffer (siempre y cuando la capacidad utilizada del mismo sea menor a N), el hilo principal intenta tomar elementos de él.
- Observo que se realiza un busy wait en ambos casos donde los hilos intentan tomar el lock sobre el buffer de manera constante en lugar de hacerlo de forma sincronizada en función de los recursos disponibles. Para el hilo hijo (productor) el “recurso disponible” equivale a la capacidad disponible del buffer, para el hilo principal (consumidor) este “recurso disponible” equivale a la presencia de elementos en el buffer.
Una solución sería hacer uso de semáforos, uno para la cantidad de elementos en el buffer y otro para los espacios disponibles en el mismo. Inicialmente el valor de contador (V) para el semáforo de los elementos será 0, mientras que el del semáforo correspondiente a los lugares vacíos será N, en ambos casos el conjunto de procesos comenzará vacío (L) .
- En Rust, el
MutexGuard (la variable buf) implementa el trait Drop. El lock se libera automáticamente cuando la variable sale de scope. Sin embargo, en un loop, el scope no termina hasta el final del bucle. Las llamadas explícitas a drop son necesarias porque los hilos se van a dormir (thread::sleep). Si no liberaran el buf antes del sleep, mantendrían el Mutex bloqueado, impidiendo que el otro hilo acceda al recurso, lo cual detendría el programa por completo.
sem_espacios_disponibles = Semaphore(N);
sem_elementos = Semaphore(0);
mutex_buffer = Mutex::new();
/* Hilo Productor (Hijo) */
loop {
sem_espacios_disponibles.acquire(); // Espera si el buffer está lleno
mutex_buffer.lock();
buffer.push(dato);
mutex_buffer.unlock();
sem_elementos.release(); // Avisa que hay un nuevo elemento
}
/* Hilo Consumidor (Principal) */
loop {
sem_elementos.acquire(); // Espera si el buffer está vacío
mutex_buffer.lock();
let dato = buffer.pop();
mutex_buffer.unlock();
sem_espacios_disponibles.release(); // Avisa que hay un espacio libre
}


- Falso, el estado interno de un actor es accesible únicamente por el mismo actor, siendo innecesario el uso de locks sobre ellos.
- Falso, por cada poll que se realiza sobre un Future se busca avanzar lo máximo posible el progreso de una tarea, es decir, hasta que deje de devolver Pending y devuelva Ready.
- Falso, en fork-join resolvemos subtareas a partir de tareas más complejas y la resolución de ellas es independiente a las resoluciones de las demás, por lo tanto haciendo uso de este modelo el resultado será determinístico.
- Falso, a los spurious wakeups, un hilo puede despertarse sin que ningún otro hilo haya emitido explícitamente una señal (
notify/signal). Por este motivo, el despertar de un hilo no garantiza que la condición esperada sea verdadera ni que alguien lo haya llamado.
