threads-opt
Diferencias
Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
| threads-opt [2018/06/26 13:46] – lmateu | threads-opt [2018/06/26 13:57] (actual) – [Equivalencia entre herramientas de sincronización] lmateu | ||
|---|---|---|---|
| Línea 63: | Línea 63: | ||
| código para depositar y extraer elementos de buffer puede sufrir de data-races. | código para depositar y extraer elementos de buffer puede sufrir de data-races. | ||
| Corrija el código usando un semáforo o mutex adicional. | Corrija el código usando un semáforo o mutex adicional. | ||
| + | |||
| + | |||
| + | === Segunda solución correcta y eficiente de la cena de filósofos === | ||
| + | |||
| + | La segunda solución se basa en que el deadlock solo se produce si los 5 filósofos llegan a cenar. | ||
| + | Si se limita a 4 los filósofos que pueden cenar, ya no habrá deadlock. | ||
| + | con 4 tickets iniciales: | ||
| + | |||
| + | < | ||
| + | pthread_mutex_t palitos[5]; | ||
| + | sem_t portero; /* con 4 tickets iniciales */ | ||
| + | | ||
| + | void filosofo(int i) { | ||
| + | for (;;) { | ||
| + | sem_wait(& | ||
| + | pthread_mutex_lock(& | ||
| + | pthread_mutex_lock(& | ||
| + | comer(i, (i+1)%5); | ||
| + | pthread_mutex_unlock(& | ||
| + | pthread_mutex_unlock(& | ||
| + | sem_post(& | ||
| + | pensar(); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | El problema de esta solución es que solo se aplica a 5 filósofos y 5 palitos. | ||
| + | generalizar a un número variable de threads accediendo a múltiples estructuras | ||
| + | de datos. | ||
| + | |||
| + | ==== Equivalencia entre herramientas de sincronización ==== | ||
| + | |||
| + | Los semáforos y monitores (mutex + condición) son equivalentes, | ||
| + | problema se puede resolver con una herramienta, | ||
| + | herramienta. | ||
| + | que no se puede implementar con las semáforos por sí solos. | ||
| + | |||
| + | Probaremos la equivalencia implementando un semáforo a partir de un monitor y un monitor | ||
| + | a partir de un semáforo. | ||
| + | |||
| + | === Semáforo implementado con monitores === | ||
| + | |||
| + | < | ||
| + | #include < | ||
| + | |||
| + | typedef struct { | ||
| + | int tickets; | ||
| + | pthread_cond_t vacio; /* Condicion para esperar entrar al semáforo */ | ||
| + | pthread_mutex_t mutex; | ||
| + | } Semaphore; | ||
| + | |||
| + | void sema_init(Semaphore *sema, int ini) { | ||
| + | sema-> | ||
| + | pthread_cond_init(& | ||
| + | pthread_mutex_init(& | ||
| + | } | ||
| + | |||
| + | void sema_wait(Semaphore *sema) { | ||
| + | pthread_mutex_lock(& | ||
| + | while (sema-> | ||
| + | pthread_cond_wait(& | ||
| + | |||
| + | sema-> | ||
| + | pthread_mutex_unlock(& | ||
| + | } | ||
| + | |||
| + | void sema_post(Semaphore *sema) { | ||
| + | pthread_mutex_lock(& | ||
| + | sema-> | ||
| + | pthread_cond_signal(& | ||
| + | pthread_mutex_unlock(& | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Observe que se uso signal en vez de broadcast. | ||
| + | tipo de threads esperando: threads que esperan obtener un ticket. | ||
| + | una sola condición. | ||
| + | que esperan extraer un ítem del buffer y los que esperan depositar un ítem. | ||
| + | se necesitan 2 condiciones, | ||
| + | broadcast en vez de signal. | ||
| + | |||
| + | === Ejercicio === | ||
| + | |||
| + | El problema de la implementación de más arriba es que un thread T que se despierta | ||
| + | después de un sem_wait no necesariamente obtiene el ticket recién depositado | ||
| + | con sem_post. | ||
| + | ocurrir que llega un nuevo thread U que invoca sem_wait y le roba el mutex a T. | ||
| + | El resultado es que U obtiene el ticket a pesar de que T lleva mucho más tiempo | ||
| + | esperándolo. | ||
| + | |||
| + | Modifique su implementación de modo que se garantice que el thread T reciba el ticket. | ||
| + | Base su implementanción en la metáfora de la Isapre: para evitar las colas hay un | ||
| + | distribuidor de números en la entrada y un visor que indica a quién se atiende | ||
| + | actualmente. | ||
| + | y espera hasta que el visor indique al menos ese número. | ||
| + | |||
| + | === Monitores a partir de semáforos === | ||
| + | |||
| + | La siguiente es una implementación de un verdadero monitor, el que fusiona el mutex | ||
| + | con la condición, a la manera de los monitores de Java. | ||
| + | |||
| + | < | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | typedef struct node { | ||
| + | sem_t wait; | ||
| + | struct node *next; | ||
| + | } Node; | ||
| + | |||
| + | typedef struct { | ||
| + | sem_t mutex; | ||
| + | Node *head; | ||
| + | } Monitor; | ||
| + | |||
| + | void mon_init(Monitor *mon) { | ||
| + | sem_init(& | ||
| + | mon-> | ||
| + | } | ||
| + | |||
| + | void mon_lock(Monitor *mon) { | ||
| + | sem_wait(& | ||
| + | } | ||
| + | |||
| + | void mon_unlock(Monitor *mon) { | ||
| + | sem_post(& | ||
| + | } | ||
| + | |||
| + | void mon_wait(Monitor *mon) { | ||
| + | Node node; | ||
| + | sem_init(& | ||
| + | node.next= mon-> | ||
| + | mon-> | ||
| + | sem_post(& | ||
| + | sem_wait(& | ||
| + | sem_destroy(& | ||
| + | sem_wait(& | ||
| + | } | ||
| + | |||
| + | void mon_broadcast(Monitor *mon) { | ||
| + | Node *node; | ||
| + | for (node= mon-> | ||
| + | sem_post(& | ||
| + | mon-> | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Este código no efectúa ninguna validación con respecto al buen uso de los monitores, | ||
| + | es decir que las operaciones mon_unlock, mon_wait y mon_broadcast solo se invoquen | ||
| + | cuando se obtuvo previamente el monitor con mon_lock. | ||
| + | |||
| + | === Ejercicio === | ||
| + | |||
| + | Implemente a partir de semáforos un monitor con múltiples condiciones, | ||
| + | de los mutex y condiciones de pthreads. | ||
| + | |||
| + | |||
| + | === Solución lectores/ | ||
| + | |||
| + | Esta solución evita la hambruna por medio de la metáfora de la isapre. | ||
| + | isapre los threads deben pedir un número antes de realizar su operación. | ||
| + | solo puede entrar a realizar su operación cuando el número que aparece en un visor | ||
| + | coincide con el número que se le asignó. | ||
| + | se satisfacen en orden FIFO (//first in first out//), aunque las lecturas se hacen | ||
| + | en paralelo y por lo tanto las salidas pueden ocurrir en un orden no FIFO. | ||
| + | |||
| + | En esta solución los lectores no pueden concertarse para causar hambruna a un escritor | ||
| + | porque una vez que el escritor recibe su número, ningún otro lector podrá entrar | ||
| + | al diccionario. | ||
| + | que salir y será el turno del escritor. | ||
| + | |||
| + | < | ||
| + | pthread_mutex_t mutex; | ||
| + | pthread_cond_t cond; | ||
| + | int readers= 0; | ||
| + | int display= 0; | ||
| + | int serial= 0; | ||
| + | | ||
| + | void enterRead() { | ||
| + | int myNum; | ||
| + | pthread_mutex_lock(& | ||
| + | myNum= = serial++; | ||
| + | while (myNum!=display) | ||
| + | pthread_cond_wait(& | ||
| + | readers++; | ||
| + | display++; | ||
| + | pthread_cond_broadcast(& | ||
| + | pthread_mutex_unlock(& | ||
| + | } | ||
| + | | ||
| + | void exitRead() { | ||
| + | pthread_mutex_lock(& | ||
| + | readers--; | ||
| + | if (readers==0) | ||
| + | pthread_cond_broadcast(& | ||
| + | pthread_mutex_unlock(& | ||
| + | } | ||
| + | | ||
| + | void enterWrite() { | ||
| + | int myNum; | ||
| + | pthread_mutex_lock(& | ||
| + | myNum= serial++; | ||
| + | while (readers> | ||
| + | pthread_cond_wait(& | ||
| + | pthread_mutex_unlock(& | ||
| + | } | ||
| + | | ||
| + | void exitWrite() { | ||
| + | pthread_mutex_lock(& | ||
| + | display++; | ||
| + | pthread_cond_broadcast(& | ||
| + | pthread_mutex_unlock(& | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Nota: el broadcast se necesita acá porque el incremento de display podría gatillar | ||
| + | que un lector que se encontraba esperando ahora puede entrar. | ||
| + | |||
| + | // | ||
| + | Supongamos que hay un escritor trabajando y n lectores esperando. | ||
| + | caso se pueden requerir hasta O(n^2) cambios de thread a thread para que todos los lectores comiencen | ||
| + | a trabajar. | ||
| + | |||
| + | |||
threads-opt.1530020771.txt.gz · Última modificación: por lmateu
