El presente ejercicio tiene por explorar las ventajas que ofrece la variable de condición a la hora de trabajar evitar esperas activas. La idea básica es procesar una estructura de datos larga y repartir el trabajo en varias hebras que potencialmente pueden ser reutilizadas. Hay 4 hebras que se encargan cada una de ellas de procesar 1/4 de la tabla de entrada.
El hilo realiza las siguientes operaciones:
Se bloquea y espera a que haya algo que hacer. Para ello utiliza pthread_cond_wait
.
Cada hebra calcula el resultado de su parte de procesado de la tabla.
La hebra escribe su parte parcial de la suma a la variable global sum
.
Al final, cuando termina la hebra, espera a que todas acaben con pthread_cond_wait
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | /****************************************************************************** * * compile with gcc -pthread *.c -o loops * test with valgrind --tool=helgrind ./loops * ******************************************************************************/ #include <pthread.h> #include <stdio.h> #include <stdlib.h> #define NTHREADS 4 #define ARRAYSIZE 100000000 #define ITERATIONS ARRAYSIZE / NTHREADS double sum=0.0; double a[ARRAYSIZE]; pthread_mutex_t sum_mutex; pthread_cond_t threads_cond; int work_pending=0; //No work int work_done=0; //All done void *do_work(void *tid) { int i, start, *mytid, end; double mysum=0.0; for(;;) { pthread_mutex_lock (&sum_mutex); while((work_pending==0)) { pthread_cond_wait(&threads_cond,&sum_mutex); } if(work_pending<0) { pthread_mutex_unlock(&sum_mutex); pthread_exit(NULL); } work_pending--; pthread_mutex_unlock(&sum_mutex); mytid = (int *) tid; start = (*mytid * ITERATIONS); end = start + ITERATIONS; printf ("\n[Thread %5d] To Work.Doing iterations \t%10d to \t %10d",*mytid,start,end-1); for (i=start; i < end ; i++) { a[i] = i * 1.0; mysum = mysum + a[i]; } /* Lock the mutex and update the global sum, then exit */ pthread_mutex_lock (&sum_mutex); sum = sum + mysum; work_done--; //printf ("\n[Thread %5d] Work done %d",*mytid,work_done); pthread_cond_broadcast(&threads_cond); pthread_mutex_unlock (&sum_mutex); pthread_mutex_lock (&sum_mutex); while(work_done!=0) { pthread_cond_wait(&threads_cond,&sum_mutex); } pthread_mutex_unlock (&sum_mutex); }//end_for pthread_exit(NULL); } int main(int argc, char *argv[]) { int i; int tids[NTHREADS]; pthread_t threads[NTHREADS]; pthread_attr_t attr; /* Pthreads setup: initialize mutex and explicitly create threads in a joinable state (for portability). Pass each thread its loop offset */ pthread_mutex_init(&sum_mutex, NULL); pthread_cond_init(&threads_cond, NULL); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for (i=0; i<NTHREADS; i++) { tids[i] = i; pthread_create(&threads[i], &attr, do_work, (void *) &tids[i]); } printf ("\n\n[MAIN][STEP 1] Let's Assign work to all threads\n"); //Init the threads pthread_mutex_lock (&sum_mutex); work_pending=NTHREADS; work_done=NTHREADS; // pthread_cond_broadcast(&threads_cond); pthread_mutex_unlock(&sum_mutex); printf ("\n\n[MAIN][STEP 2] Let's Wait for the result\n"); //Wait for the result pthread_mutex_lock (&sum_mutex); while(work_done!=0) { //printf("Main to wait for the result %d",work_done); pthread_cond_wait(&threads_cond,&sum_mutex); } pthread_mutex_unlock(&sum_mutex); printf ("\n\n[MAIN][STEP 3] Output. Sum= %e\n", sum); printf ("\n\n[MAIN][STEP 4] Let's Signal exit\n"); pthread_mutex_lock(&sum_mutex); work_pending=-1; //To exit pthread_cond_broadcast(&threads_cond); pthread_mutex_unlock(&sum_mutex); /* Wait for all threads to complete */ for (i=0; i<NTHREADS; i++) { pthread_join(threads[i], NULL); } pthread_attr_destroy(&attr); pthread_cond_destroy(&threads_cond); pthread_mutex_destroy(&sum_mutex); pthread_exit (NULL); } |
Vamos a explorar el código de forma práctica para realizar ciertos cambios:
Examine el código, compílelo con el gcc y ejecútelo. Calcule el tiempo de ejecución con 1 hebra, 2 hebras y 4 hebras.
Modifique el código para que trabaje para que los hilos procesen varias tablas. Pista: Para realizar esta operación puede crear una estructura de datos donde pase como parámetro de entrada la tabla a las diferentes hebras del sistema, en un bucle.
La solución utiliza una notificación en broadcast para despertar a los diferentes hilos. Cambiela para que utilice
pthread_cond_signal
.