POSIX Threads
POSIX Threads
Hemos visto procesos y algunas formas para compartir información entre procesos. Ahora veremos cómo usar múltiples hebras o hilos de control
para efectuar múltiples tareas dentro de un mismo proceso. Un aspecto ineludible al hablar de compartir recursos son los mecanismos de
sincronización necesarios para conservar la consistencia de los recursos compartidos.
Objetivos: Aprender conceptos básicos sobre Hilos, experimentar con los llamados a hilos de POSIX, conocer los mecanismos de sincronización de
POSIX.
POSIX significa Portable Operating System Interface (for Unix). Es un estándar orientado a facilitar la creación de aplicaciones aplicaciones
confiables y portables. La mayoría de las versiones populares de UNIX ( Linux, Mac OS X) cumplen este estándar en gran medida. La biblioteca para
el manejo de hilos en POSIX es pthread.
Se puede manejar eventos asíncronos asignando un hilo a cada tipo de evento. Luego cada hilo maneja sus eventos en forma sincrónica.
Los hilos de un proceso comparten el mismo espacio de direcciones y descriptores de archivos.
Procesos con múltiples tareas independientes pueden terminar antes si estas tareas se desarrollan traslapadamente en hilos separados. De
este modo tiempos de espera de la primera tarea no retrasan la segunda.
Programas interactivos pueden lograr mejor tiempo de respuesta usando hilos para manejar la entrada y salida. Este es un ejemplo del
punto previo.
La creación de un hilo es mucho más rápida y toma menos recursos que la creación de un proceso.
ID del hilo. No son únicos dentro del sistema, sólo tienen sentido en el contexto de cada proceso.
Stack pointer
Un conjunto de registros
Propiedades de itineración (como política y prioridad)
Conjunto de señales pendientes y bloquedas.
Datos específicos del hilo.
Para hacer uso de estas funciones incluir <pthread.h>, el ligado debe incluir -l thread , como en cc -o ejecutable -l thread fuente.c
Identificación de Hilos
Así como un proceso tiene un PID (Process Identification), cada hilo tiene un identificador de hilo. Mientras los PID son enteros no negativos, el
ID de un hilo es dependiente del SO y puede ser una estructura. Por esto para su comparación se usa una función.
#include <pthread.h> Compara dos identificados de hilos tid1 y tid2
int pthread_equal(pthread_t tid1, pthread_t tid2);
Retorna: no cero si es igual, cero en otro caso
#include <pthread.h> Para obtener identificador de un hilo
pthread_t pthread_self(void);
Retorna: la ID del hilo que la llamó
Creación de Hilos
Los procesos normalmente corren como un hilo único. La creación de un nuevo hilo se logra vía pthread_create.
# include <pthread.h>
int pthread_create(pthread_t * restrict tidp, tidp: salida, puntero a id del hilo
const pthread_attr_t * restrict attr, attr: entrada, para definir atributos del hilo, null para default
void * ( * start_routine ) (void *), start_routine: entrada, función a correr por el hilo
void * restrict arg); arg: entrada, argumento de la función del hilo.
La función debe retornar un * void, el cual es interpretado
como el estatus de término por pthread_join
Nota: restrict es una palabra reservada para señalar al compilador que el dato referenciado sólo será accedido en la función a través de ese
puntero. Esto permite al compilador hacer optimización.
Ejemplo: Impresión de thread ID. Puede bajar todos los fuentes del texto guía, una mirada al Makefile es educativa, se aprende a crear una
biblioteca, y cómo generar una compilación parametrizada por la plataforma en que usted trabaje.
Notar: El hilo principal debe dormir para asegurar que el hilo logre hacer su parte. El hilo nuevo debe llamar a pthread_self() porque no hay
garantía que el valor pasado por el hilo principal lo tenga. Todo esto por desconocer qué hilo corre primero.
Término de un Hilo
Si un hilo invoca a exit, _Exit o _exit, todo el proceso terminará.
Un hilo puede terminar de tres maneras sin terminar el proceso: Retornando de su rutina de inicio, cancelado por otro hilo del mismo proceso, o
llamando pthread_exit.
#include <pthread.h> rval_ptr queda disponible para otros hilos al llamar pthread_join
void pthread_exit (void * rval_ptr); rval_ptr debe existir después del término del hilo.
int pthread_join(pthread_t tid, void ** rval_ptr); El hilo llamante se bloquea hasta el término del hilo indicado.
Si el hilo en cuestión es cancelado, rval_prt toma el valor
PTHREAD_CANCELED
Si no estamos interesados en el valor retornado, poner NULL.
int pthread_cancel(pthread_t tid); Permite a un hilo cancelar otro hilo del mismo proceso.
Retorno 0 es OK, !=0 => error.
Equivale a si el hilo indicado llamara
pthread_exit(PTHREAD_CANCELED); sin embargo, un hilo puede
ignorar este requerimiento o controlar cómo se cancela.
pthread_cancel sólo hace un requerimiento, pero no lo fuerza.
int pthread_setcacelstate(int state, int * oldstate); Permite cambiar el estado del hilo a
PTHREAD_CANCEL_ENABLE (default) o
PTHREAD_CANCEL_DISABLE, en este estado el hilo ignora
llamados a pthread_cancel que le afecten.
Ejemplos: Recuperando el estatus de término de hilos: exitstatus.c
No debemos usar variables automáticas (aquellas en stack) como en pthread_exit, ver badexit2.c
Ejemplos:
0.- fork v/s threads: Comparación entre creación de procesos y creación de hilos.
1.- callcopymalloc.c programa que crea un hilo y copia un archivo. Funciones relacionadas copyfilemalloc.c, copyfile y otras en restart.c
2.- callcopypass.c ídem anterior, el parámetro pasado es un arreglo con espacio para el valor retornado. Función relacionada copyfilepass.c
3.- copymultiple.c programa que llama copyfilepass en varios hilos para generar múltiples copias de un archivo.