D Caso Estudio:
Pthreads
Sistemas Concurrentes y Paralelos
Grado de informática
EPS - Universidad de Lleida
1
D.1 PThreads: POSIX Threads
2
Librería de hilos en Unix
◼ Existen dos bibliotecas principales para la gestión de
hilos en Unix: threads Sun Solaris y threads POSIX.
– Ambas bibliotecas contemplan la creación y destrucción dinámica
de los hilos.
– Vamos a trabajar con POSIX threads dada su mayor
disponibilidad / portabilidad.
◼ Proporciona servicios para:
– Crear y destruir hilos
– Paso de mensajes y datos entre hilos
– Sincronización entre hilos
– Planificar la ejecución del hilo
• Transferir el control de un hilo a otro
• Guardar y restaurar el contexto del hilo
3
Características Pthreads
◼ Un hilo tiene asociado:
– TID (identificador del hilo)
– Pila
– Prioridad de ejecución.
– Dirección de inicio de ejecución.
◼ Un hilo se representa dentro del sistema
mediante su identificador TID (pthread_t).
◼ En algunos sistemas se deben enlazar con la
librería pthread:
> gcc Hilos.c -o Hilos -lpthread
4
Funciones básicas gestión
pthreads
◼ Las llamadas a sistema para la gestión de hilos
normalmente devuelven un 0 si todo funciona
correctamente y un 1 si se produce un error. 5
Creación Hilos (I)
◼ Crea un hilo que ejecutará la rutina start_routine a la cual
se le pasarán un conjunto de argumentos (arg).
◼ Parámetros:
– tid: variable en donde se guardará el identificador del thread
que se crea.
– attr: atributos asignados al thread creado (tamaño pila,
prioridad, política de planificación, ect…).
– start_routine: Rutina que ejecutará el hilo.
– arg: Argumentos que se pasarán a la rutina start_routine.
6
Creación Hilos (II)
◼ Los parámetros que se pasan a la función del hilo
son de tipo apuntador a void.
◼ Los hilos también pueden devolver un valor de
retorno (apuntador a void).
◼ Si se quiere modificar los atributos de los hilos, es
imprescindible inicializar los atributos antes de crear
los hilos y pasárselos en el segundo argumento.
7
Finalización Hilos
◼ Provoca que el hilo que la invoca espere la finalización de
otro hilo (especificado mediante le parámetro thread).
◼ Permite especificar dependencias entre hilos, de forma
que varios hilos puedan cooperar en la realización de una
determinada tarea.
◼ En value_ptr se guarda el valor de retorno del hilo que ha
finalizado.
◼ Dependiendo de los atributos, esta función debe ser
invocada para permitir la liberación de la memoria
asociada al hilo.
8
Ejemplo Hilos: “Hola Mundo”
/* HolaPThreads.c – Pthreads "hola, Mundo” */
#include <stdio.h>
Atributos Hilo
#include <stdlib.h>
(normalmente NULL)
#include <pthread.h>
void *howdy(void *vargp);
Argumentos Hilo
int main() {
(void *p)
pthread_t tid;
pthread_create(&tid, NULL, howdy, NULL);
pthread_join(tid, NULL);
exit(0);
} Valor retorno
/* Función hilo */ (void **p)
void *howdy(void *vargp) {
printf("Hola, mundo!\n");
return NULL;
}
9
Ejecución Hilos: “Hola Mundo”
Hilo principal
call Pthread_create()
Pthread_create() returns Hilo hijo
call Pthread_join()
printf()
hilo principal espera a return NULL;
que el hilo hijo finalice (finaliza hilo hijo)
Pthread_join()
returns
exit()
finaliza
hilo principal y
los hilos hijos
10
Tutorial: Primera aplicación multi-hilo
Enunciado
◼ Implementar una aplicación multihilo, formada por 2
hilos cada uno de los cuales muestre sucesivamente por
pantalla un digito numérico
– El primer hilo mostrará el digito "1", el segundo hilo el digito "2"
– Los hilos devolverán el número de carácteres imprimidos
◼ Pasos:
– Diseño función para los hilos
– Crear 2 hilos de ejecución (pthread_create):
• Especificar función a ejecutar por el hilo
• Especificar atributos nulos
• Especificar parámetros del hijo (digito a mostrar)
– Esperar la finalización del hilo y mostrar el valor de retorno.
11
Tutorial: Primera aplicación multi-hilo
Versión Secuencial
/* FirstSec.c – Primera aplicación multi-hilo }
Secuencial */
#include <stdio.h>
#include <stdlib.h>
#define DMaxDigitos 1000
int MostrarDigitos(int *dig); int MostrarDigitos(int dig)
{
int main() int x;
{ char cad[2];
int dig1,dig2; sprintf(cad,"%d",dig);
dig1=MostrarDigitos(1); for(x=0;x<DMaxDigitos;x++)
dig2=MostrarDigitos(2); {
printf("Dig. 1 mostrados: %d.\n",dig1); write(1,cad,1);
printf("Dig. 2 mostrados: %d.\n",dig2); fsync(1);
}
exit(0); return (x);
}
12
Tutorial: Primera aplicación multi-hilo
Consejos y Preguntas
◼ Consejos:
– Vigilar que la función que implementa el hilo tenga el prototipo
correcto: void *func_hilo(void *arg)
– Revisar que se enlaza la librería pthreads al compilar (-lpthread)
– Al pasar los parámetros a los hijos no se puede utilizar la misma
variable
– El valor de retorno de los hilos no puede ser una variable local,
tiene que ser una variable global o memoria dinámica
◼ Preguntas:
– ¿De cuantos hilos consta la aplicación?
– ¿Diferencias en la ejecución respecto a la secuencial?
– Ejecutar varias veces la aplicación, ¿El comportamiento se
mantiene? 13
Tutorial: Primera aplicación multi-hilo
Solución
/* FirstMultiThreading.c – Primera
aplicación multi-hilo */ printf("\nDig. 1 Mostrados:
%d.\n",*dig1);
#include <stdio.h>
printf("Dig. 2 Mostrados:
#include <stdlib.h>
%d.\n",*dig2);
#define DMaxDigitos 1000 free(dig1); free(dig2);
int* MostrarDigitos(char *car); exit(0);
int main() }
{
pthread_t Tid1,Tid2; int* MostrarDigitos(char *car)
int *dig1,*dig2; {
int x, *ret;
/* Creación Hilos*/ for(x=0;x<DMaxDigitos;x++)
pthread_create(&Tid1, NULL, write(1,car,1);
MostrarDigitos,"1");
pthread_create(&Tid2, NULL, ret = malloc(sizeof(int));
MostrarDigitos,"2"); *ret = x;
pthread_join(Tid1,&dig1); return (ret);
pthread_join(Tid2,&dig2); }
14
Otras funciones pthreads
◼ Obtener el identificador del hilo:
()
◼ Finalizar la ejecución de un hilo, indicando el estado el
parámetro value:
◼ Cancelar un hilo por su Tid:
15
Atributos Hilos (I)
◼ Los atributos de los hilos nos permiten definir como se comportarán
durante su ejecución:
– Tamaño pila (stacksize): especifica el tamaño de la pila que utilizará el hilo.
– Dirección pila (stackaddr): especifica la dirección en donde empieza la pila del
hilo.
– Modo finalización (detachstate): define si la finalización es controlada (hasta que
no se realice el pthread_join, el hilo no puede acabar) o no.
• PTHREAD_CREATE_DETACHED: El hilo se crea en modo separado (detached)
• PTHREAD_CREATE_JOINABLE: El hilo se crea en modo unido (joinabled)
– Ámbito prioridades (scope): define el ámbito en el que compite el hilo:
• PTHREAD_SCOPE_SYSTEM: Compite contra todos los hilos del sistema
• PTHREAD_SCOPE_PROCESS: Compite con todos los hilos de su proceso
– Herencia parámetros planificación (inheritsched): define si hereda los
parámetros de planificación del hilo padre (PTHREAD_INHERIT_SCHED) o se
utilizan los específicados en los atributos (PTHREAD_EXPLICIT_SCHED).
16
Atributos Hilos (II)
– Política de planificación (schedpolicy): especifica la política de planificación
del hilo: SCHED_FIFO, SCHED_RR, y SCHED_OTHER
– Parámetros de planificación (schedparam): Define los parámetros de
planificación para el hilo (la prioridad de ejecución).
◼ Inicializar y liberar los atributos:
– Permiten crear y liberar la estructura de datos que utiliza POSIX para
gestionar los atributos de los hilos.
pthread_attr_t attr;
pthread_attr_init(&attr);
………………………………
pthread_attr_destroy(&attr);
17
Modificación atributos hilos
◼ Varios hilos
pueden compartir
los mismos
atributos.
◼ La modificación de
un atributo
concreto se realiza
mediante la
función
correspondiente.
19
Ejemplo modificación atributos Hilo
◼ Se modifica la aplicación multihilo anterior, cambiando algunos de
los atributos de los hilos:
– Hilo 1:
• Modo finalización: Separado (detached)
• Herencia parámetros planificación: Explicita y de alcance del proceso
• Tamaño pila hilo: 1 KB.
– Hilo 2:
• Modo finalización: Unido (joinable)
• Herencia parámetros planificación: Explicita y de alcance del proceso
• Tamaño pila hilo: 1 KB.
– Ante cualquier error, cancelar el hilo superviviente.
◼ Pasos:
– Inicializar y asignar atributos
– Crear 2 hilos de ejecución (pthread_create):
• Especificar atributos definidos
20
Ejemplo modificación atributos Hilo
int main()
{
pthread_t Tid1,Tid2;
int *dig1,*dig2, err;
pthread_attr_t attr;
pthread_attr_init(&attr);
/* Inicialización Atributos*/
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS);
pthread_attr_setstacksize(&attr, 1024);
pthread_create(&Tid1, &attr, (void * (*)(void *)) MostrarDigitos,"1");
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&Tid2, &attr, (void * (*)(void *)) MostrarDigitos,"2");
21
Ejemplo modificación atributos Hilo
err = pthread_join(Tid1, (void **)&dig1);
if (err!=0) {
fprintf(stderr,"\nError join Hilo 1 (%d).\n",err);
pthread_cancel(Tid2);
}
else {
printf("\nDigito 1 Mostrados: %d.\n",*dig1);
free(dig1);
}
err = pthread_join(Tid2, (void **)&dig2);
if (err!=0)
fprintf(stderr,"\nError join Hilo 2 (%d).\n",err);
else {
printf("\nDigito 2 Mostrados: %d.\n",*dig2);
free(dig2);
}
exit(0);
}
22
Ejercicio: Implementación
sumatorio concurrente (I)
◼ Implementar una aplicación concurrente multihilo
capaz de calcular el sumatorio de los N primeros
números mediante una reducción
– Diseño flexible de forma que pueda utilizar
cualquier número de hilos M ≤ N
◼ Pasos:
– Diseñar función sumatorio Hilos
– Distribuir trabajo entre los hilos
– Crear hilos de ejecución
– Esperar e integrar resultados parciales hilos.
24
Ejercicio: Implementación
sumatorio concurrente (II)
◼ Consejos:
– Cuidado con el paso de parámetros, usad memoria
dinámica (malloc)
– No os olvidéis de liberar la memoria dinámica (free)
cuando ya no la necesitéis
– No modifiquéis el fichero main.c
Para compilar y ejecutar el programa:
gcc -pthread main.c sumar_sec.c; ./[Link] 20000 4
25
Ejercicio: Implementación sumatorio
concurrente
Versión Secuencial
#include <stdio.h> long int Sumar(long int inicio, long int fin)
#include <stdlib.h> {
long int x, suma;
#define DDefaultN 100
suma = 0;
long int Sumar(long int inicio, long int fin); for(x=inicio; x<=fin; x++)
int main(int argc, char *argv[]) suma += x;
{
long int N, SumaTotal; return (suma);
}
if (argc>1) N = atol(argv[1]);
else N = DDefaultN;
SumaTotal = Sumar(1, N);
printf("Sumatorio 1 a %ld: %ld.\n", N,
SumaTotal);
exit(0);
}
26