threads
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 [2018/06/26 13:53] – [La cena de los filósofos] lmateu | threads [2018/07/06 00:04] (actual) – [Productor/consumidor] lmateu | ||
---|---|---|---|
Línea 359: | Línea 359: | ||
} | } | ||
| | ||
- | void consumidor(Buffer | + | void *consumidor(void *ptr) { // porque se usa en pthread_create |
+ | Buffer *buf= ptr; | ||
for (;;) { | for (;;) { | ||
Cuadro *cuadro= get(buf); | Cuadro *cuadro= get(buf); | ||
Línea 366: | Línea 367: | ||
mostrarCuadro(cuadro); | mostrarCuadro(cuadro); | ||
} | } | ||
+ | return NULL; | ||
} | } | ||
| | ||
void reproducirVideo() { | void reproducirVideo() { | ||
Buffer *buf= nuevoBuffer(60); | Buffer *buf= nuevoBuffer(60); | ||
- | | + | |
- | pthread_create(& | + | pthread_create(& |
productor(buf); | productor(buf); | ||
- | pthread_join(pid, NULL); | + | pthread_join(t, NULL); |
} | } | ||
</ | </ | ||
Línea 912: | Línea 914: | ||
El problema de esta solución es que los lectores se pueden concertar para entrar y | El problema de esta solución es que los lectores se pueden concertar para entrar y | ||
salir alternadamente, | salir alternadamente, | ||
- | así en // | + | así en // |
- | + | que evitan | |
- | === Segunda solución === | + | |
- | + | ||
- | 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 | + | |
- | 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 | + | |
- | a trabajar. | + | |
- | + | ||
- | ==== 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 | + | |
- | 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. | + | |
threads.1530021206.txt.gz · Última modificación: 2018/06/26 13:53 por lmateu