image.png

  1. No se trata de un busy-wait, en el caso de error se ejecuta un sleep lo cual provoca que el hilo sea desalojado del procesador. En caso de establecer correctamente la conexión vuelve a iterar sin sleep pero connect es bloqueante.
  2. Al comienzo de cada iteración se hace un sleep del hilo provocando que el mismo sea desalojado del procesador, además se hace uso de lock() el cual es bloqueante y provoca que la ejecución del hilo se retome cuando el mismo pueda tomar el lock del recurso, por lo tanto no se trata de un busy wait.
  3. En este caso se observa que se lanzan N threads los cuales intentan tomar el lock de un recurso para modificar el valor del mismo mediante write() este caso tampoco representa un busy wait porque además del sleep que se observa en cada iteración de cada hilo (de entre 3 y 7 segundos) el uso de write es bloqueante de modo que el hilo continuará su ejecución cuando el lock esté disponible.

image.png

Red de Petri para el problema del Lector-Escritor sin preferencia:

image.png

Red de Petri para el problema del Lector-Escritor con preferencia de escritura:

image.png

image.png

Como entidades podemos identificar a las mesas, mozos, cocina y depósito. La idea es que los mozos puedan atender mesas, notificar pedidos a la cocina, llevar pedidos a las mesas y cobrar a las mismas. Por otro lado los clientes deben poder hacer pedidos y la cuenta a los mozos. Por parte de la cocina, la misma debe ser capaz atender pedidos que traigan los mozos y buscar ingredientes al depósito. Por otro lado el depósito recibirá mensajes para ingresar al mismo.

/* Actores */

struct Mesa {
		id: usize,
		mozo_a_cargo: Recipient<Result>
}

struct Mozo {
		id: usize,
		pedidos_mesas: HashMap<usize, Pedido>,
		cocina: Recipient<Result>
}

struct Cocina {
		pedidos_mesas: HashMap<usize, Pedido>,
}

struct Deposito {
		cocina: Recipient<Result>
}

/* Mensajes */

struct Pedido {
		id_mesa: usize,
		platos: HashMap<i32, i32>, // Este es un diccionario cuya clave es el ID del plato y su valor la cantidad solicitada del mismo.
		mesa_solicitante: Recipient<Result>
}

struct SolicitudCuenta {
		id_mesa: usize,
		mesa_solicitante: Recipient<Result>
}

struct SolicitudIngredientes {
		id_mesa: usize
}

struct RespuestaIngredientes {
		id_mesa: usize
}

struct Cuenta {
		monto: f64
}

/* Handles */

impl Handler<Pedido> for Mozo {
    type Result = ();

    fn handle(&mut self, msg: Pedido, _ctx: &mut Context<Self>) -> Self::Result {
				let p_id_mesa = msg.0;
        let p_platos = msg.1;
        let p_mesa_solicitante = msg.2;

        println!("[MOZO {}] Tengo el pedido de la mesa {}, se lo paso a cocina.", self.id, id_mesa);
        
        pedidos_mesas[p_id_mesa] = p_platos;
        
				cocina.try_send(Pedido {id: p_id_mesa, platos: p_platos, mesa_solicitante: p_mesa_solicitante} );
    }
}

impl Handler<Pedido> for Cocina{
    type Result = ();

    fn handle(&mut self, msg: Pedido, _ctx: &mut Context<Self>) -> Self::Result {
				let p_id_mesa = msg.0;
        let p_platos = msg.1;
        let p_mesa_solicitante = msg.2;

        println!("[COCINA {}] Vamos a preparar el pedido de la mesa {}, busquemos los ingredientes en el depósito.", id_mesa);
        pedidos_mesas[p_id_mesa] = p_platos;
        
				deposito.try_send(SolicitudIngredientes { id_mesa: p_id_mesa, mesa_solicitante} );
    }
}

impl Handler<SolicitudCuenta> for Mozo {
    type Result = ();

    fn handle(&mut self, msg: SolicitudCuenta, _ctx: &mut Context<Self>) -> Self::Result {
				let p_id_mesa = msg.0;
        let p_mesa_solicitante = msg.1;

        println!("[MOZO {}] Me piden la cuenta de la mesa {}, voy a buscar el ticket.", self.id, id_mesa);
        
        let monto_total = 0.00;
        let pedido = pedidos_mesas[p_id_mesa];
        
        for (plato, cantidad) in pedido.platos {
		        monto_total += precios[plato] * cantidad;
        }
        
        pedidos_mesas[p_id_mesa] = null;
        
				p_mesa_solicitante.try_send(Cuenta {monto: monto_total} );
    }
}

impl Handler<Cuenta> for Mesa {
    type Result = ();

    fn handle(&mut self, msg: Cuenta, _ctx: &mut Context<Self>) -> Self::Result {
        println!("[MESA {}] Acá tiene los ${}, nos vemos !.", self.id, msg.0);
    }
}

image.png

  1. Falso → Los procesos sí tienen espacios de memoria aislados entre sí. Sin embargo, los hilos de un mismo proceso comparten el mismo espacio de direccionamiento (heap, variables globales). Las tareas asincrónicas son unidades lógicas que corren sobre hilos, por lo que también comparten la memoria del proceso.
  2. Falso → El scheduler del SO no tiene visibilidad sobre las tareas asincrónicas; solo aloja y desaloja threads. Quien detiene y ejecuta tareas asincrónicas es el Executor o Runtime de la aplicación (como Tokio en Rust). El SO puede suspender el hilo completo que está ejecutando la tarea, pero no "intercambiar" la ejecución de tareas individuales dentro de ese hilo.
  3. Falso → Los threads sí disponen de un stack propio pero las tareas se ejecutan dentro de un mismo thread de forma concurrente.
  4. Falso→ En este caso contamos con una única CPU, por lo tanto lo que puede suceder es que la programación de las tareas asíncronas cuente con el uso de yield de forma que se busque mejorar la ejecución concurrente de las mismas. Sin embargo, el context-switch de los threads agrega overhead y hay que considerarlo debido a que contamos con una única CPU, por lo tanto no podemos asegurar que con threads el tiempo de ejecución será significativamente menor.

image.png

  1. Fork-Join → El procesamiento de cada archivo .DOC equivale a una sub-tarea aislada e independiente, además mediante work-stealing podemos optimizar el procesamiento en caso de que algún thread se haya quedado sin tareas por realizar.
  2. Actores → Cada usuario dentro de una partida es un actor, también podríamos representar un actor coordinador. Cada usuario al contestar una pregunta lo que hace es enviar un mensaje al coordinador con su respuesta, el coordinador lo procesa y en función de la respuesta y el tiempo que haya tardado el usuario en responder actualiza la tabla de puntuaciones. Una vez recibidas todas las respuestas envía un mensaje hacia cada usuario con la información de la tabla actualizada (esta información la visualizan los usuarios una cantidad de tiempo X) y la próxima pregunta para que nuevamente inicie el ciclo.