Ejercicios de producto-consumidor y la mesa de los filósofos con Semaphore.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
KyMAN f0c01b8d1e feat(md): Agregada cabecera en el README. 2 months ago
Ejercicio_1/TareaProductorBufferConsumidor rebuild: Reconstruidos los JAR. 2 months ago
Ejercicio_2/TareaCenaFilosofos rebuild: Reconstruidos los JAR. 2 months ago
LICENSE Initial commit 2 months ago
README.md feat(md): Agregada cabecera en el README. 2 months ago

README.md

PSP02_Tarea

Este ejercicio fue hecho sin mirar el temario y entregado fuera de plazo por motivos agenos al ciclo, por lo que independientemente de resultados, agradecer al profesor a cargo de la asignatura por mirarlo igualmente pese a dichas condiciones, así por el apoyo ofrecido. También quiero que quede de aviso para otros posibles compañeros que quieran hacer uso de este contenido por posiblemente no ceñirse a lo requerido en la asignatura. Sea como sea, espero sea útil para aprender y reciclarse como lo fue para mi.

Ejercicios de producto-consumidor y la mesa de los filósofos con Semaphore. Dichos ejercicios están subidos a la siguiente URL Git:

producto-consumidor

Para este ejercicio se usó un simple String en vez de un vector o ArrayList de elementos para simplificar puesto que se hace uso de caracteres.

Tanto el productos como el consumidor, pese a ser dos hilos distintos, ambos no son más que objetos anónimos de tipo Thread volcados sobre un Overwrite del "run" de un Runnable. El proceso es muy rápido por lo que al ejecutar no habrá opción a nada hasta que acabe de trabajar. En este caso, ambos comparten una variable de tipo String como entorno de Buffer para compartir esa memoria de caracteres donde el productor genera nuevos caracteres al final de la cadena y el consumidor irá consumiendo desde el principio de la misma siguiendo las normas del ejericio las cuales entendí como:

  • El productor no puede tener más de 6 caracteres en la cadena de memoria o Buffer compartido.
  • El consumidor cogerá hasta 15 caracteres para generar un producto.

Al tener esas dos limitaciones, el programa se detendrá automáticamente cuando el consumidor consiga los 15 caracteres y el productor tenga 6 caracteres en el Buffer.

flowchart TD

B[Buffer]
P[Productor]
C[Consumidor]
A{Añade}
K{Coge}

P --> A
A -->|"Si Buffer < 6"| B
C --> K
K -->|"Si Buffer < 15 && \nConsumidor > 0"| B

La extracción de cada caracter se hace a partir de las letras inglesas mayúsculas de ASCII, es decir, a partir del caracter 65 hasta el 90, cogiendo un valor aleatorio entre 0 y 25.

La configuración es la siguiente:

  • longitud_limite_producto: Determina la longitud máxima del producto.
  • longitud_maxima_buffer: Determina la longitud máxima del Buffer.

Problema de los Filósofos

Para este ejercicio, al no tener ningún objeto que cláramente requiera de datos específicos más que acciones comunes, se optó por hacer uso de objetos anónimos de tipo Thread contra objeto Runnable con el Override de su Run para establecer el proceso en cuestión.

En este ejercicio entiendo que una mesa redonda de filósofos tienen 3 acciones a desarrollar en bucle a lo largo del tiempo de forma ininterrumpida, las cuales son:

  • Pensar: Acción que se simulará a partir de un Sleep con un tiempo aleatorio por cada ciclo.
  • Tener hambre: Esta acción básicamente, a parte de ser un simple aviso al usuario confirme se cambia de estado a esperar a tener sus dos palillos disponibles, es el proceso de espera en la cual el filósofo espera su turno para poder hacer uso de sus palillos que dando simultáneamente, tanto el de su lado derecho como el de su lado izquierdo libres. Básicamente es un proceso de espera no controlado condicionado a tener sus palillos libres sin acaparar dichos palillos.
  • Comer: Como la acción de pensar, en este caso sería una simulación a partir de un Sleep con un tiempo aleatorio por cada ciclo.

Aquí planteo que ambos palillos queden libres pues si se hace uso de uno que le quede libre y espera al siguiente, este proceso puede bloquear la aplicación entendiendo la casuística de que por azar, cada filósofo coja un palillo y éstos estén en la espera de que el siguiente deje de hacer uso de los mismos, bloqueando infinitamente la aplicación, además de la falta de control para poder cerrar la aplicación de forma correcta.

Al ver que es un proceso infinito, para poder tener un control de detención de la aplicación se hará uso de la terminal donde si ponemos alguna de las claves "exit", "close", "cerrar", "terminar", "bye", "detener", "finalizar" o "stop", el programa detendrá todos los procesos y se finalizará independientemente del estado en el que se encuentre. Para poder llevar a cabo dicha acción, todo irá condicionado a una variable estática común llamada "trabajando" que determinará si está o no activa a modo de protocolo "Worker".

En caso de poner cualquier otro comando no contempleado en esta guía, el terminal imprimirá su desconocimiento.

En este caso, los palillos serán los semáforos que regulan el consumo de procesos. Se entenderá que habrá un palillo por cada filósofo, y éstos los compartirán con sus vecinos inmediatos de mesa.

Un filósofo será un método para que éste almacene sus propios valores en su ejecución independiente de los demás (Encapsulación). Cada Filósofo constará de un par de punteros a sus palillos correspondientes para agilizar el acceso a éstos. Una vez dentro del hilo de procesos del propio filósofo, éste estará pensando un tiempo aleatorio, seguido de solicitar los palillos (Tener hambre y hacer uso de los semáforos) mediante el método tryAcquire de Semaphore por el hecho de que nos permite obtener si hemos conseguido retener ambos palillos en un tiempo simbólico que impida que retener uno de los dos palillos indefinidamente. Una vez con los dos palillos, se hará un tiempo de espera aleatorio conforme está comiendo y se liberarán ambos palillos. Tras esto se volverá a repetir el proceso infinitamente.

Es cierto que los Filósofos pueden ser objetos, pero con objeto de simplificar en la medida de lo posible el/los Scripts de la tarea, y por la naturaleza y uso de los atributos que éstos llevarían, se obvió su creación como tal. Lo mismo puede ir aplicado a los palillos y al gestor de la terminal.

Se ha creado una instrucción de gestión de tiempo equivalente al Thread.sleep llamada esperar con motivo de que si se quiere detener el programa, éste gestione el sleep de forma intermitente, permitiendo el cierre de la aplicación inmediatamente.

No se hizo uso de "wait" por el hecho de que éste habría de estar implementado en una clase-objeto, cosa que se deshechó anteriormente por la naturaleza de las acciones y valores a utilizar.

flowchart TD

P[Pensar]
H["Tener hambre"]
PI["Palillo izquierdo"]
PD["Palillo derecho"]
PA(["¿Ambos palillos libres?"])
C[Comer]

P --> H
H -..-|"Si mis palillos \nestán libres"| C
H -->|"¿Está libre?"| PI
H -->|"¿Está libre?"| PD
PI -->|Sí| PA
PD -->|Sí| PA
PI -->|No| PA
PD -->|No| PA
PA -->|Sí| C
PA -->|No| H
C -->|"Volver a empezar"| P

La configuración del ejercicio es la siguiente:

  • numero_de_filosofos: Cantidad de filósofos que se procesarán. Mínimo 2.
  • tiempo_minimo_de_espera: Tiempo mínimo de espera en milisegundos por los que se hará comprobación de que la aplicación sigue activa en el método "espera" equivalente a un "Thread.sleep".
  • tiempo_de_pensar: Tiempo máximo en segundos dedicado a pensar. Éste valor es el que se usará para determinar un valor aleatorio de espera entre 0 y dicho número.
  • tiempo_para_comer: Tiempo máximo en segundos dedicado para comer. Éste valor es el que se usará para determinar un valor aleatorio de espera entre 0 y dicho número.
  • tiempo_limite_para_palillo: Tiempo límite en milisegundos en espera de que un palillo quede libre.

F.A.Q.

¿Las aplicaciones pueden dar errores al alterar su configuración?

Las aplicaciones no tienen un control de errores profundo por la falta de tiempo por lo que pueden saltar errores cara una mala configuración o cara valores no esperados.