Escuela Superior de Ingenieros Universidad de Sevilla HILOS 1. OBJETIVO El objetivo es estudiar los mecanismos para la creacin y destruccin de hilos (procesos ligeros) de un mismo proceso, as como la sincronizacin entre stos mediante mutex y variables condicionales. 2. DESCRIPCIN os servicios para la utilizacin de proceso ligeros (hilos) se pueden agrupar de la siguiente !orma" #ervicios para la gestin de atributos de un proceso ligero. $reacin e identi!icacin de procesos ligeros. %erminacin de procesos ligeros. #incronizacin entre procesos ligeros de un mismo proceso. 2.1 SERVICIOS PARA LA GESTIN DE ATRIBUTOS DE UN PROCESO LIGERO $uando se crea un hilo, si no se le pasa un objeto del tipo atributo, se crea con los atributos por de!ecto. #i se &uiere crear un hilo con otros valores para estos atributos (distintos a los de por de!ecto), previamente se debe tener un objeto atributo (donde se almacenan los valores de los di!erentes atributos), inicializarlo, darle los valores apropiados, y pas'rselo a la !uncin &ue crea el hilo. os atributos se almacenan en un objeto atributo de tipo pthread_attr_t. ( continuacin se describen los principales servicios relacionados con los atributos de un proceso ligero. Creacin y de!r"ccin de a!ri#"!$% )ara la creacin y el inicio de un objeto atributo (utilizado en la creacin de un proceso ligero) se utiliza la siguiente !uncin" int pthread_attr_init(pthread_attr_t *attr); )ara la destruccin del objeto de tipo atributo se utiliza" int pthread_attr_destroy(pthread_attr_t *attr); E!a#&eci'ien!$ y $#!encin de& e!ad$ de !er'inacin% El servicio para el establecimiento (set) del atributo correspondiente al estado de terminacin es" int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); El valor del argumento detachstate puede ser" PTHREAD_CREATE_DETACHED: El proceso ligero &ue se cree con este estado de terminacin se considerar' independiente y liberar' sus recursos cuando !inalice su ejecucin. PTHREAD_CREATE_JOINA!E: El proceso ligero &ue se cree con este estado de terminacin se considerar' como no independiente y no liberar' sus recursos cuando !inalice su ejecucin. En este caso, es necesario &ue otro proceso espere por su !inalizacin utilizando pthread_"#in. El servicio para la obtencin ($et) del atributo correspondiente al estado de terminacin es el siguiente" int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); E!a#&eci'ien!$ y $#!encin de& !a'a($ de &a )i&a% $ada hilo tiene una pila cuyo tama*o se puede establecer en el momento de la creacin, d'ndole al atributo &ue se va a utilizar en la creacin el valor adecuado. Esto se hace mediante el servicio" int pthread_attr_setstacksize (pthread_attr_t *attr, int stac%si&e); El servicio para obtener el tama*o de la pila es el siguiente" int pthread_attr_getstacksize (pthread_attr_t *attr, int *stac%si&e); 2.2 CREACIN E IDENTI*ICACIN DE PROCESOS LIGEROS )ara crear un proceso ligero &ue ejecuta una determinada !uncin se utiliza el siguiente servicio" int pthread_create(pthread_t *thread, pthread_attr_t *attr, '#id *(*start_r#(tine) ('#id *), '#id *ar$); El primer argumento debe ser la direccin de memoria de una variable (o memoria reservada) donde esta !uncin almacenar' el identi!icador del hilo &ue se crea, y este argumento es del tipo puntero a pthread_t. El segundo argumento especi!ica los atributos de ejecucin asociados al nuevo proceso ligero. #i este argumento es N)!! se tomar'n los atributos por de!ecto, &ue incluyen la creacin del proceso como no independiente (JOINA!E). El tercer argumento indica el nombre de la !uncin a ejecutar cuando el proceso ligero comienza su ejecucin. Esta !uncin re&uiere un solo par'metro &ue se especi!ica con el cuarto argumento, ar$. )ara obtener el identi!icador de un hilo se utiliza el servicio" pthread_t pthread_self('#id); 2.+ TER,INACIN DE PROCESOS LIGEROS os servicios relacionados con la terminacin de procesos ligeros son dos" para esperar por la terminacin de un proceso ligero y para !inalizar la ejecucin de un proceso ligero. )ara esperar por la terminacin de un proceso ligero se utiliza la !uncin" int pthread_join(pthread thid, '#id *'a*(e); Este servicio permite esperar a &ue termine un hilo. a !uncin suspende la ejecucin del hilo llamante hasta &ue el hilo con identi!icador thid !inaliza su ejecucin. +evuelve en el segundo argumento el valor devuelto por el hilo &ue termina (mediante pthread_e+it, comentado a continuacin). ,nicamente se puede solicitar el servicio pthread_"#in sobre hilos creados como no independientes. )ara !inalizar la ejecucin de un hilo se utiliza" '#id pthread_exit('#id *'a*(e); El argumento es un puntero a una estructura &ue es devuelta al hilo &ue ha ejecutado la correspondiente llamada a pthread-join. ( continuacin se presentan varios ejemplos &ue muestran la utilizacin de estos servicios. )ara obtener el ejecutable, es necesario usar la opcin ,*pthread al enlazar el programa" $cc pr(e-a.c /# pr(e-a /*pthread E-e')&$ 1" programa &ue crea dos procesos ligeros y espera su !inalizacin. 0inc*(de 1pthread.h2 0inc*(de 1stdi#.h2 '#id 3(nc('#id) 4
print3(5Thread 6d 7n5, pthread_se*3()); pthread_e+it(N)!!); 8 int 9ain('#id) 4 pthread_t th:, th;; <* se crean d#s pr#ces#s *i$er#s c#n atri-(t#s p#r de3ect# *< pthread_create(=th:, N)!!, 3(nc, N)!!); pthread_create(=th;, N)!!, 3(nc, N)!!); print3(5E* pr#ces# *i$er# principa* c#ntin(a e"ec(tand#7n5); <* se espera s( ter9inaci>n *< pthread_"#in(th:, N)!!); pthread_"#in(th;, N)!!); ret(rn(?); 8 prueba..c Estudiar el ejemplo anterior. /#on necesarias las variables th: y th;0 E-e')&$ 2" programa &ue crea diez procesos ligeros independientes. 0inc*(de 1pthread.h2 0inc*(de 1stdi#.h2 0inc*(de 1(nistd.h2 0de3ine @AA_THREADB :? '#id 3(nc('#id) 4 print3(5Thread 6d 7n5, pthread_se*3()); pthread_e+it(N)!!); 8 int 9ain('#id) 4 int "; pthread_attr_t attr; pthread_t thidC@AA_THREADBD; <* Be inician *#s atri-(t#s E se 9arcan c#9# independientes *< pthread_attr_init(=attr); pthread_attr_setdetachstate(=attr, PTHREAD_CREATE_DETACHED);
3#r(" F ?; " 1 @AA_THREADB; " GG) pthread_create(=thidC"D, =attr, 3(nc, N)!!); pthread_attr_destr#E(=attr); <* E* pr#ces# *i$er# principa* n# p(ede esperar *a 3ina*i&aci>n *< <* de *#s pr#ces#s *i$er#s H(e ha cread# E se s(spende d(rante (n *< <* ciert# tie9p# esperand# s( 3ina*i&aci>n *< s*eep(I); ret(rn (?); 8 prueba1.c Estudiar el ejemplo anterior. /2u pasa si el hilo principal termina antes &ue los procesos ligeros creados0 E-e')&$ +" programa &ue crea un proceso ligero independiente por cada n3mero &ue se da por teclado hasta introducir 4. 0inc*(de 1pthread.h2 0inc*(de 1stdi#.h2 '#id i9pri9ir(int *n) 4 print3(5Thread 6d 6d 7n5, pthread_se*3(), *n); pthread_e+it(N)!!); 8 int 9ain('#id) 4 pthread_attr_t attr; pthread_t thid; int n(9F:;
Jhi*e(n(9) 4 print3(5Escri-ir n(9er# enter# :7n5); scan3(56d5, =n(9); <* espera *< pthread_create(=thid, =attr, i9pri9ir, =n(9); 8 pthread_attr_destr#E(=attr); ret(rn ?; 8 prueba5.c Estudiar el ejemplo anterior. /2u pasara si el hilo creado tardara en imprimir m's &ue el usuario en dar un nuevo n3mero por teclado0 2.. SINCRONI/ACIN ENTRE PROCESOS LIGEROS os mecanismos de sincronizacin &ue se utilizan con los procesos ligeros son los mutex (sem'!oros binarios) y las variables condicionales. %ambin se pueden utilizar los sem'!oros normales ya vistos anteriormente. 6na variable condicional o!rece un mecanismo para &ue los hilos esperen el cumplimiento de predicados en los &ue intervengan variables compartidas. as variables compartidas se deber'n proteger con un mutex. $ada vez &ue un hilo modi!ica una de estas variables compartidas, debe se*alizar (utilizando pthread_c#nd_si$na*) mediante la variable condicional &ue se ha realizado un cambio. Esta se*al activar' a un hilo en espera (&ue se habra &uedado blo&ueado utilizando pthread_c#nd_Jait) &ue debe comprobar de nuevo si ahora se cumple su predicado. a espera en la variable condicional conlleva la liberacin autom'tica del mutex asociado para &ue otros hilos puedan modi!icar las variables compartidas. $uando se activa un hilo en espera se vuelve a competir por el mutex. )ara utilizar un mutex, un programa debe declarar una variable de tipo pthread_9(te+_t e iniciarla antes de utilizarla. +e !orma an'loga, para utilizar en un programa una variable condicional es necesario declarar una variable de tipo pthread_c#nd_t e iniciarla antes de utilizarla. ( continuacin se exponen los servicios utilizados para el manejo de los mutex y de las variables condicionales. Iniciar "n '"!e0 int pthread_mutex_init(pthread_9(te+_t *9(te+, pthread_9(te+attr_t *attr); El segundo argumento son los atributos con los &ue se crea inicialmente el mutex. #i es N)!! se consideran los atributos por de!ecto. De!r"ir "n '"!e0 int pthread_mutex_destroy(pthread_9(te+_t *9(te+); O)eracin #a-ar 1c$')e!ir )$r e& '"!e02 int pthread_mutex_lock(pthread_9(te+_t *9(te+); O)eracin "#ir 1&i#erar e& '"!e02 int pthread_mutex_unlock(pthread_9(te+_t *9(te+); Iniciar "na 3aria#&e c$ndici$na& int pthread_cond_init(pthread_c#nd_t *c#nd, pthread_c#ndattr_t *attr); El segundo argumento son los atributos con los &ue se crea inicialmente la variable condicional. #i es 76 se consideran los atributos por de!ecto. De!r"ir "na 3aria#&e c$ndici$na& int pthread_cond_destroy(pthread_c#nd_t *c#nd); E)erar en "na 3aria#&e c$ndici$na& int pthread_cond_wait(pthread_c#nd_t *c#nd, pthread_9(te+_t *9(te+); Esta !uncin suspende al proceso ligero hasta &ue otro proceso ejecute una operacin de se*alizacin sobre la variable condicional pasada como primer argumento. +e !orma atmica se libera el mutex pasado como segundo argumento. $uando el proceso se despierte volver' a competir por el mutex. Se(a&ar "na 3aria#&e c$ndici$na& int pthread_cond_signal(pthread_c#nd_t *c#nd); #e despierta a un proceso suspendido en la variable condicional pasada como argumento (al despertarse tiene &ue competir por el mutex). 7o tiene e!ecto si no hay ning3n proceso ligero esperando en la variable condicional. E-e')&$ ." )roductor8consumidor usando mutex y variables condicin. 0inc*(de 1pthread.h2 0inc*(de 1stdi#.h2 0de3ine @AA_)KKER :?;L <* ta9aM# de* -(33er *< 0de3ine DATOB_A_PROD)CIR :????? <* dat#s a pr#d(cir *< '#id Pr#d(ct#r('#id); '#id C#ns(9id#r('#id); pthread_9(te+_t 9(te+; <* 9(te+ para c#ntr#*ar e* acces# a* -(33er c#9partid# *< pthread_c#nd_t n#_**en#; <* esperar si n# estN **en# *< pthread_c#nd_t n#_'aci#; <* esperar si n# estN 'acO# *< int n_e*e9ent#sF?; <* nP9er# de e*e9ent#s en e* -(33er *< int -(33erC@AA_)KKERD; <* -(33er c#9Pn *< int 9ain(int ar$c, char *ar$'CD)4 pthread_t th:, th;; pthread_9(te+_init(=9(te+, N)!!); pthread_c#nd_init(=n#_**en#, N)!!); pthread_c#nd_init(=n#_'aci#, N)!!); pthread_create(=th:, N)!!, Pr#d(ct#r, N)!!); pthread_create(=th;, N)!!, C#ns(9id#r, N)!!); pthread_"#in(th:, N)!!); pthread_"#in(th;, N)!!); pthread_9(te+_destr#E(=9(te+); pthread_c#nd_destr#E(=n#_**en#); pthread_c#nd_destr#E(=n#_'aci#); e+it(?); 8 <* c>di$# de* pr#d(ct#r *< '#id Pr#d(ct#r('#id) 4 int dat#, i ,p#s F ?; 3#r(iF?; i1DATOB_A_PROD)CIR; iGG ) 4 dat# F i; <* pr#d(cir dat# *< pthread_9(te+_*#c%(=9(te+); <* acceder a* -(33er *< Jhi*e (n_e*e9ent#s FF @AA_)KKER) <* si -(33er **en# *< pthread_c#nd_Jait(=n#_**en#, =9(te+); <* se -*#H(ea *< -(33erCp#sD F i; p#s F (p#s G :) 6 @AA_)KKER; n_e*e9ent#s F n_e*e9ent#s G :; i3 (n_e*e9ent#s FF :) pthread_c#nd_si$na*(=n#_'aci#); <* -(33er n# 'acO# *< pthread_9(te+_(n*#c%(=9(te+); print3(5Pr#d(ce 6d 7n5, dat#); <* pr#d(ce dat# *< 8 pthread_e+it(?); 8 <* c>di$# de* c#ns(9id#r *< '#id C#ns(9id#r('#id) 4 int dat#, i ,p#s F ?; 3#r(iF?; i1DATOB_A_PROD)CIR; iGG ) 4 pthread_9(te+_*#c%(=9(te+); <* acceder a* -(33er *< Jhi*e (n_e*e9ent#s FF ?) <* si -(33er 'acO# *< pthread_c#nd_Jait(=n#_'aci#, =9(te+); <* se -*#H(ea *< dat# F -(33erCp#sD; p#s F (p#s G :) 6 @AA_)KKER; n_e*e9ent#s F n_e*e9ent#s / : ; i3 (n_e*e9ent#s FF @AA_)KKER / :); pthread_c#nd_si$na*(=n#_**en#); <* -(33er n# **en# *< pthread_9(te+_(n*#c%(=9(te+); print3(5C#ns(9e 6d 7n5, dat#); <* c#ns(9e dat# *< 8 pthread_e+it(?); 8 prueba9.c Estudiar el ejemplo anterior. 6tilizar los mecanismos de sincronizacin entre hilos para &ue en la prueba 5 no haya problemas con la variable num. :ealizar un cliente para el servidor de !icheros sin estados creado en la pr'ctica correspondiente a :)$;s &ue copie un !ichero en otro utilizando n hilos &ue de !orma concurrente envan las peticiones al servidor. os argumentos de entrada son n, el !ichero origen y el !ichero destino.