Tema 1 - Threads - Apunte PTHREADS PDF
Tema 1 - Threads - Apunte PTHREADS PDF
Tema 1 - Threads - Apunte PTHREADS PDF
Pthreads
Pthreads es una librería que cumple los estándares POSIX y que nos permite trabajar con distintos hilos
de ejecución(threads) al mismo tiempo en un mismo proceso( programa).
El standard POSIX
POSIX es el acrónimo de Portable Operating System Interface, viniendo la X de UNIX..
Estos son una familia de standares de llamadas al sistema que tienen como finalidad generalizar las
interfaces de los sistemas operativos para que las aplicaciones se ejecuten en distintas plataformas.
Si por el contrario no estamos usando el compilador de GNU, lo mejor sera que miremos la pagina man
del compilador de C instalado en el sistema. Por ejemplo, en el caso del compilador cc de la mayoria
de las distribuciones de linux:
cc prog_con_pthreads.c -o prog_con_pthreads -pthread
Función pthread_create.
El prototipo de la función es el siguiente:
int pthread_create(pthread_t * thread, pthread_attr_t *attr, void * (*start_routine)(void *), void *arg)
✔ thread: Es una variable del tipo pthread_t que contendrá los datos del thread y que nos servirá para
identificar el thread en concreto cuando nos interese hacer llamadas a la libreria para llevar a cabo
alguna acción sobre él.
✔ attr: Es un parámetro del tipo pthread_attr_t y que se debe inicializar previamente con los atributos
que queramos que tenga el thread. Entre los atributos figuiran: la prioridad, el quantum!!,y el
algoritmo de planificación que queramos usar!!!!!. Si pasamos como parámetro aquí NULL, la
librería le asignará al thread unos atributos por defecto..
✔ start_routine: Aquí pondremos la dirección de la función que queremos que ejecute el thread. La
función debe devolver un puntero genérico (void *) como resultado, y debe tener como único
parámetro otro puntero genérico.
✔ arg: Es un puntero al parámetro que se le pasará a la función. Puede ser NULL si no queremos
pasarle nada a la función.
Como siempre en C en caso de que todo haya salido bien, la función devuelve un 0 o un valor distinto
de 0 en caso de que hubo algun error.
Para que nuestro programa se comporte según las anteriores opciones disponemos de dos funciones
más de la librería: pthread_join y pthread_detach.
Función pthread_join:Esta función suspende el thread llamante hasta que no termine su ejecución el
thread indicado por th. Además, una vez éste último termina, pone en thread_return el resultado
devuelto por el thread que se estaba ejecutando.
➢ th: Es el identificador del thread que queremos esperar, y es el mismo que obtuvimos al crearlo con
pthread_create.
➢ thread_return: Es un puntero a un puntero que apunta (valga la redundancia) al resultado devuelto
por el thread que estamos esperando cuando terminó su ejecución. Si este parámetro es NULL, le
estamos indicando a la librería que no nos importa el resultado.
Función pthread_detach:Esta función es para desligar a un proceso de los threads que haya creado .
Le indica a la librería que NO queremos que nos guarde el resultado de la ejecución del thread indicado
por th. Por defecto la librería guarda el resultado de ejecución de todos los threads hasta que nosotros
hacemos un pthread_join para recoger el resultado. De esta manera una vez que el thread haya
terminado la librería eliminará los datos del thread de sus tablas internas y tendremos más espacio
disponible para crear otros threads
main()
{
pthread_t thread1, thread2;
char *message1 = "Thread 1";
char *message2 = "Thread 2";
int iret1, iret2;
Problemas de concurrencia
Cuando decidimos trabajar con programas concurrentes uno de los mayores problemas con los que nos
podremos encontrar, y que es inherente a la concurrencia, es el acceso a variables y/o estructuras
compartidas o globales
Las funciones que ofrece Pthreads para llevar esto a cabo son:
Función pthread_mutex_init:Esta función inicializa un mutex. Hay que llamarla antes de usar
cualquiera de las funciones que trabajan con mutex.
El prototipo de la función es el siguiente:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
✔ mutex: Es un puntero a un parámetro del tipo pthread_mutex_t, que es el tipo de datos que usa la
librería Pthreads para controlar los mutex.
✔ attr: Es un puntero a una estructura del tipo pthread_mutexattr_t, y sirve para definir qué tipo de
mutex queremos: normal, recursivo o errorcheck (En este apunte solo veremos semáforos mutex y
recursivos), si este valor es NULL , la librería le asignará un valor por defecto.
Función pthread_mutex_lock:Esta función pide el bloqueo para entrar en una RC. Si queremos
implementar una RC, todos los thread tendrán que pedir el bloqueo sobre el mismo semáforo.
El prototipo de la función es el siguiente:
int pthread_mutex_lock(pthread_mutex_t *mutex)
✔ mutex: Es un puntero al mutex sobre el cual queremos pedir el bloqueo o sobre el que nos
bloquaremos en caso de que ya haya alguien dentro de la RC.
Thread 1
Thread 2
void *funcion_Thread_2(void *arg)
{
...
pthread_mutex_lock(&mutex_2);
/* Comienzo Sección critica de código */
...
/* Fin Sección critica de código *
pthread_mutex_unlock(&mutex_1);
...
}
Semáforos recursivos
Estos semáforos solo aceptarán una sola petición de bloqueo por el mismo thread. Si el mismo thread
hace 10 llamadas a pthread_mutex_lock sobre el mismo semáforo, luego tendrá que hacer 10 llamadas
a pthread_mutex_unlock, es decir, tantas como haya hecho a pthread_mutex_lock.
En cambio, los del tipo recursivo solo aceptarán una sola llamada a pthread_mutex_lock. Las siguientes
llamadas serán ignoradas.
Para poder crear un semáforo recursivo, tendremos que decírselo a pthread_mutex_init, indicándole
como atributo el resultado de una llamada a pthread_mutexattr_settype. El procedimiento es:
Si creemos que la siguiente llamada a pthread_mutex_lock va a ser bloqueante y que puede provocar
un deadlock, la librería de Pthreads nos ofrece una función más para comprobar si eso es cierto:
pthread_mutex_trylock.
Semáforos contadores
La libreria phtreads no contiene un mecanismo de sincronización entre procesos con semáforos para
ello contiene variables condición que es un mecanismo de sincronización similar pero que escapa al
alcance del curso.
Para sincronizar procesos y/o contar recursos utilizaremos otra libreria de semáforos que nos provee de
los semáforos contadores.
Semáforos contadores: Estos semáforos pueden incializarse en un valor mayor a 0. Sirven como
contadores que tiene valores no negativos y que deben iniciarse antes de ser usados. Estos semáforos
existen a en la especificacion de POSIX.1b
En resumen para proteger regiones críticas e implementar exclusión mutua utilizaremos los mutex
de pthreads, para sincronizar procesos, contar recursos.etc utilizaremos la libreria semaphore.h que
figura a partir de la versión 1b del standard POSIX.
RECORDAR: Importar siempre la libreria de de semaforos, en los imports de nuestro programa que
usa threads -> #include <semaphore.h>
Algunas operaciones permitidas sobre ellos son:
Función sem_post:Incrementa el valor del semáforo y es la clásica señal (signal) de operación del
semáforo.
int sem_wait(sem_t * sem);
✔ *sem: Es un puntero a sem_t, el semáforo que estamos incializando.