5. Pthreads
Texto íntegro de la conversión. Las figuras y la disposición de las notas se pueden consultar en el PDF.
Página 42
5.Pthreads
Hoy día los sistemas operativos suelen proporcionar directamente implementaciones de flujos. Varios lenguajes de programación también facilitan la programación si los tienen integrados en su sintaxis. No obstante, aquí presentamos los flujos definidos por el estándar POSIX, denominados pthreads.
La ventaja de realizar implementaciones con pthreads radica en la gran portabilidad que se puede obtener, dada la gran implantación de estos flujos en la práctica totalidad de los sistemas.
Existen muchas primitivas relacionadas con la gestión de los pthreads, pero aquí nos centraremos en las más básicas. Lo primero que hemos de conocer son las primitivas que permiten crear (pthread_create), finalizar (pthread_exit) y sincronizar (pthread_join) pthreads. Una llamada a pthread_join bloqueará al pthread que la haga hasta que el pthread con el que se pide sincronizar no haya acabado. Además, la llamada le devolverá información sobre el estado de finalización que el pthread haya indicado al hacer la llamada pthread_exit. Si no se quiere esperar a un pthread, se puede indicar por medio de la primitiva pthread_detach.
Además, se puede conseguir información sobre el identificador del pthread utilizando la primitiva pthread_self.
Primitiva
pthread_create
pthread_exit
pthread_join
pthread_detach
pthread_self
Hay que guardar el valor de retorno indicado al hacer un pthread_exit hasta que se haga un pthread_join o un pthread_detach. Por ello, la estructura de datos que representa al pthread no será liberada hasta después de ejecutar la llamada que llegue más tarde de la combinación pthread_exit y pthread_join en caso de que queramos una sincronización, o pthread_exit y pthread_detach en caso de que no queramos una sincronización. Hemos de ser conscientes de que, como con cualquier otro re-
Notas y pies de figura de la fuente
Descripción Creación de un pthread Terminación de un pthread Espera la finalización de un pthread Indica que no se querrá esperar al pthread Retornar el identificador del pthread
Página 43
curso, es necesario que se libere la estructura de datos del pthread cuando deje de necesitarse. Por ello, como diseñadores de programas que usan pthreads hemos de tener en cuenta estos aspectos.
Como los pthreads de un proceso comparten memoria, se pueden comunicar rápidamente a través de ella. Pero eso supone la necesidad de utilizar mecanismos de sincronización y exclusión mutua. Es conveniente conocer como mínimo algunas primitivas que permiten proteger la modificación de estructuras de datos de manera concurrente y la implementación de regiones críticas. Eso se puede hacer forzando el acceso a las regiones críticas en exclusión mutua (mutual exclusion o mutex). Para ello existen unas variables de tipo pthread_mutex_t sobre las que se puede pedir el acceso de manera exclusiva en un momento determinado utilizando la primitiva pthread_mutex_lock (adquirir el lock sobre el mutex). Una vez la llamada retorna, tenemos la garantía de que el pthread que ha hecho la llamada es el único que tiene derecho a realizar las acciones que queremos proteger con esta variable de mutex y puede entrar en la región crítica y ejecutar las operaciones que ésta contiene. Al acabar estas operaciones, hay que liberar la variable de mutex utilizando pthread_mutex_unlock con el fin de permitir el acceso a la región crítica a otros pthreads que estén esperando.
Primitiva
pthread_mutex_lock
pthread_mutex_unlock
A continuación mostramos un ejemplo sencillo disponible en múltiples sitios de Internet que ilustra el uso de las llamadas básicas para conseguir crear 10 pthreads que trabajen de modo concurrente, pero modifiquen una variable compartida en exclusión mutua, garantizando que el resultado final de un contador sea equivalente a una ejecución en serie.
#include <stdio.h> #include <pthread.h>
#define NTHREADS 10
void *thread_function(); pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; int counter = 0;
main() {
pthread_t thread_id[NTHREADS]; int y, j; for(i=0; y < NTHREADS; i++)
{
Notas y pies de figura de la fuente
Ved también En el módulo 7, trataremos a fondo el problema de la sincronización y de la exclusión mutua. Descripción Pedir acceso en exclusividad al recurso mutex Liberar recurso mutex
Página 44
pthread_create( &thread_id[i], NULL, &thread_function, NULL); }
for(j=0; j < NTHREADS; j++) { pthread_join( thread_id[j], NULL);
} /* Now that all threads are complete I can print the final result. / / Without the join I could be printing en value before all the threads */
/* have been completed. */ printf("Final counter value: %d\n", counter);
}
void *thread_function()
{ printf("Thread number %ld\n", pthread_self()); pthread_mutex_lock( &mutex1);
counter++; pthread_mutex_unlock( &mutex1); }
Ver fragmento extraído sin normalizar
## Página 42
<!-- source-page: 42 -->
GNUFDL • PID_00214802 42 La gestión de procesos
5.Pthreads
Hoy día los sistemas operativos suelen proporcionar directamente implemen-
taciones de flujos. Varios lenguajes de programación también facilitan la pro-
gramación si los tienen integrados en su sintaxis. No obstante, aquí presenta-
mos los flujos definidos por el estándar POSIX, denominados pthreads.
La ventaja de realizar implementaciones con pthreads radica en la gran
portabilidad que se puede obtener, dada la gran implantación de estos
flujos en la práctica totalidad de los sistemas.
Existen muchas primitivas relacionadas con la gestión de los pthreads, pe-
ro aquí nos centraremos en las más básicas. Lo primero que hemos de co-
nocer son las primitivas que permiten crear (pthread_create), finalizar
(pthread_exit) y sincronizar (pthread_join) pthreads. Una llamada a
pthread_join bloqueará al pthread que la haga hasta que el pthread con
el que se pide sincronizar no haya acabado. Además, la llamada le devolverá
información sobre el estado de finalización que el pthread haya indicado al
hacer la llamada pthread_exit. Si no se quiere esperar a un pthread, se pue-
de indicar por medio de la primitiva pthread_detach.
Además, se puede conseguir información sobre el identificador del pthread uti-
lizando la primitiva pthread_self.
Primitiva Descripción
pthread_create Creación de un pthread
pthread_exit Terminación de un pthread
pthread_join Espera la finalización de un pthread
pthread_detach Indica que no se querrá esperar al pthread
pthread_self Retornar el identificador del pthread
Hay que guardar el valor de retorno indicado al hacer un pthread_exit
hasta que se haga un pthread_join o un pthread_detach. Por ello,
la estructura de datos que representa al pthread no será liberada hasta
después de ejecutar la llamada que llegue más tarde de la combinación
pthread_exit y pthread_join en caso de que queramos una sincroniza-
ción, o pthread_exit y pthread_detach en caso de que no queramos una
sincronización. Hemos de ser conscientes de que, como con cualquier otro re-
## Página 43
<!-- source-page: 43 -->
GNUFDL • PID_00214802 43 La gestión de procesos
curso, es necesario que se libere la estructura de datos del pthread cuando deje
de necesitarse. Por ello, como diseñadores de programas que usan pthreads
hemos de tener en cuenta estos aspectos.
Como los pthreads de un proceso comparten memoria, se pueden comu- Ved también
nicar rápidamente a través de ella. Pero eso supone la necesidad de utilizar
mecanismos de sincronización y exclusión mutua. Es conveniente conocer En el módulo 7, trataremos a
fondo el problema de la sin-
como mínimo algunas primitivas que permiten proteger la modificación de cronización y de la exclusión
mutua.
estructuras de datos de manera concurrente y la implementación de regiones
críticas. Eso se puede hacer forzando el acceso a las regiones críticas en exclu-
sión mutua (mutual exclusion o mutex). Para ello existen unas variables de tipo
pthread_mutex_t sobre las que se puede pedir el acceso de manera exclusiva
en un momento determinado utilizando la primitiva pthread_mutex_lock
(adquirir el lock sobre el mutex). Una vez la llamada retorna, tenemos la ga-
rantía de que el pthread que ha hecho la llamada es el único que tiene dere-
cho a realizar las acciones que queremos proteger con esta variable de mutex y
puede entrar en la región crítica y ejecutar las operaciones que ésta contiene.
Al acabar estas operaciones, hay que liberar la variable de mutex utilizando
pthread_mutex_unlock con el fin de permitir el acceso a la región crítica a
otros pthreads que estén esperando.
Primitiva Descripción
pthread_mutex_lock Pedir acceso en exclusividad al recurso mutex
pthread_mutex_unlock Liberar recurso mutex
A continuación mostramos un ejemplo sencillo disponible en múltiples sitios
de Internet que ilustra el uso de las llamadas básicas para conseguir crear 10
pthreads que trabajen de modo concurrente, pero modifiquen una variable
compartida en exclusión mutua, garantizando que el resultado final de un
contador sea equivalente a una ejecución en serie.
#include <stdio.h>
#include <pthread.h>
#define NTHREADS 10
void *thread_function();
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int counter = 0;
main()
{
pthread_t thread_id[NTHREADS];
int y, j;
for(i=0; y < NTHREADS; i++)
{
## Página 44
<!-- source-page: 44 -->
GNUFDL • PID_00214802 44 La gestión de procesos
pthread_create( &thread_id[i], NULL, &thread_function, NULL);
}
for(j=0; j < NTHREADS; j++)
{
pthread_join( thread_id[j], NULL);
}
/* Now that all threads are complete I can print the final result. */
/* Without the join I could be printing en value before all the threads */
/* have been completed. */
printf("Final counter value: %d\n", counter);
}
void *thread_function()
{
printf("Thread number %ld\n", pthread_self());
pthread_mutex_lock( &mutex1);
counter++;
pthread_mutex_unlock( &mutex1);
}