1. Programacin en POSIX Threads (Pthreads) 1.1. Introduccin
La biblioteca de pthreads es una biblioteca que cumple los estndares POSIX y que nos permite trabajar con distintos hilos de ejecucin (threads) al mismo tiempo. La diferencia entre un thread y un proceso es que los procesos no comparten memoria entre s, a no ser que se haya declarado explcitamente usando alguno de los mecanismos de IPC (InterProcess Communication) de Unix, mientras que los threads s que comparten totalmente la memoria entre ellos. Adems, para crear threads se usan las funciones de la biblioteca pthread o de cualquier otra que soporte threads mientras que para crear procesos usaremos la llamada al sistema fork(), que se encuentra en todos los sistemas unix. Ya que pthreads es una biblioteca POSIX, se podrn portar los programas hechos con ella a cualquier sistema operativo POSIX que soporte threads. Ejemplos de ello son IRIX, los unix'es de BSD, Digital Unix OSF/1, etc. 1.2. 1.2. Como compilar un programa con pthreads Para crear programas que hagan uso de la biblioteca pthreads necesitamos, en primer lugar, la biblioteca en s. sta viene en la mayora de distribuciones linux, y seguramente se instale al mismo tiempo que los paquetes incluidos para el desarrollo de aplicaciones (es decir, cuando instalamos la libc o algn paquete tipo libc-devel) Si no es as, o usas un sistema que no sea linux, la biblioteca no debera ser muy difcil de encontrar en la red, porque es bastante conocida y se suele usar bastante. Una vez tenemos la biblioteca instalada, deberemos compilar el programa y linkarlo con la biblioteca dependiendo del compilador que estemos usando. La forma ms usual de hacer esto es, si estamos usando como compilador GNU gcc con el comando: gcc pr ogr ama_con_pt hr eads. c - o pr ogr ama_con_pt hr eads - l pt hr ead Si por el contrario no estamos usando el compilador de GNU, lo mejor ser que miremos la pgina man del compilador de C instalado en el sistema. Por ejemplo, en el caso del compilador de Digital para OSF/1, ste tiene un parmetro especial para compilar con pthreads: cc pr ogr ama_con_pt hr eads. c - o pr ogr ama_con_pt hr eads - pt hr ead Como vers la sintaxis no es muy diferente pero el compilador de Digital no aceptar la de gcc y viceversa. 1.3. 1.3. Creacin y manipulacin de threads Para crear un thread nos valdremos de la funcin pt hr ead_cr eat e de la biblioteca y de la estructura de datos pthread_t que identifica cada thread diferencindolo de los dems y que contiene todos sus datos. El prototipo de la funcin es el siguiente: int pt hr ead_cr eat e( pt hr ead_t * t hr ead, pt hr ead_at t r _t *at t r , voi d * ( *st ar t _r out i ne) ( voi d *) , voi d *ar g)
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 biblioteca para llevar a cabo alguna accin sobre l. attr: Es un parmetro del tipo pthread_attr_t y que se debe inicializar previamente con los atributos que queramos que tenga el thread. Entre los atributos hay la prioridad, el quantum, el algoritmo de planificacin que queramos usar, etc. Si pasamos como parmetro aqu NULL, la biblioteca le asignar al thread unos atributos por defecto (RECOMENDADO). start_routine: Aqu pondremos la direccin de la funcin que queremos que ejecute el thread. La funcin debe devolver un puntero genrico (void *) como resultado, y debe tener como nico parmetro otro puntero genrico. La ventaja de que estos dos punteros sean genricos es que podremos devolver cualquier cosa que se nos ocurra mediante los castings de tipos necesarios. Si necesitamos pasar o devolver ms de un parmetro a la vez, se puede crear una estructura y meter all dentro todo lo que necesitemos. Luego pasaremos o devolveremos la direccin de esta estructura como nico parmetro. (ver cdigo de ejemplo) arg: Es un puntero al parmetro que se le pasar a la funcin. Puede ser NULL si no queremos pasarle nada a la funcin. En caso de que todo haya ido bien, la funcin devuelve un 0 o un valor distinto de 0 en caso de que hubo algn error.
Una vez hemos llamado a esta funcin, ya tenemos a nuestro(s) thread(s) funcionando, pero ahora tenemos dos opciones: esperar a que terminen los threads, en el caso de que nos interese recoger algn resultado, o simplemente decirle a la biblioteca de pthreads que cuando termine la ejecucin de la funcin del thread elimine todos sus datos de sus tablas internas. Para ello, disponemos de dos funciones ms de la biblioteca: pt hr ead_j oi n y pt hr ead_det ach.
i nt pt hr ead_j oi n( pt hr ead_t t h, voi d **t hr ead_r et ur n)
Esta funcin suspende el thread llamante hasta que no termine su ejecucin el thread indicado por th. Adems, 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 puntero que apunta (valga la redundancia) al resultado devuelto por el thread que estamos esperando cuando termin su ejecucin. Si este parmetro es NULL, le estamos indicando a la biblioteca que no nos importa el resultado. Devuelve 0 en caso de todo correcto, o valor diferente de 0 si hubo algn error.
i nt pt hr ead_det ach( pt hr ead_t t h) Esta funcin le indica a la biblioteca que no queremos que nos guarde el resultado de la ejecucin del thread indicado por th. Por defecto la biblioteca guarda el resultado de ejecucin de todos los threads hasta que nosotros hacemos un pthread_join para recoger el resultado. Es por eso que si no nos interesa el resultado de los threads tenemos que indicarlo con esta funcin. As una vez que el thread haya terminado la biblioteca eliminar los datos del thread de sus tablas internas y tendremos ms espacio disponible para crear otros threads (IMPORTANTE) th: Es el identificador del thread Devuelve 0 en caso de que todo haya ido bien o diferente de 0 si hubo error.
Hasta ahora hemos estado hablando de devolver valores cuando un thread finaliza, pero an no hemos dicho como se hace. Pues bien, para ello tenemos la funcin pt hr ead_exi t
voi d pt hr ead_exi t ( voi d *r et val ) Esta funcin termina la ejecucin del thread que la llama. retval: Es un puntero genrico a los datos que queremos devolver como resultado. Estos datos sern recogidos ms tarde cuando alguien haga un pthread_join con nuestro identificador de thread. No devuelve ningn valor.
Con todo lo que hemos visto hasta ahora ya estamos preparados para hacer nuestro primer programa con pthreads. El programa de ejemplo crear MAX_THREADS threads que ejecutarn la funcin funcion_thr. Esta funcin sacar un mensaje por pantalla del tipo "hola, soy el thread nmero x", donde x ser un nmero diferente para cada thread. Para pasar esos parmetros a la funcin usaremos un struct del C, donde meteremos la cadena que debe imprimir cada thread ms su identificador. (La cadena la podramos haber metido directamente dentro de la funcin, pero as veremos como se pasa ms de un parmetro al thread) Una vez termina su ejecucin, el thread devolver como resultado su identificador (codificado en un entero), que ser imprimido por pantalla por el thread padre que esperar que todos los threads terminen.
/ *** <CORTAR AQUI > ***/ / *** Ar chi vo ej 1. c *****/ / * Cr eamos MAX_THREAD t hr eads que sacan por pant al l a una cadena y su i dent i f i cador Una vez t er mi nan su ej ecuci n devuel ven como r esul t ado su i dent i f i cador */ #i ncl ude <pt hr ead. h> #i ncl ude <st di o. h> #i ncl ude <st dl i b. h> #i ncl ude <st r i ng. h> #def i ne MAX_THREADS 10 / / t abl a con l os i dent i f i cador es de l os t hr eads pt hr ead_t t abl a_t hr [ MAX_THREADS] ; / / t i po de dat os y t abl a con l os par amet r os t ypedef st r uct { i nt i d; char *cadena; } t hr _par am_t ; t hr _par am_t par am[ MAX_THREADS] ; / / t enemos que cr ear una t abl a par a l os par met r os por que l os pasamos por / / r ef er enci a. As , si sol o t uvi r amos una var i abl e par a l os par met r os / / al modi f i car est a modi f i car amos t odas l as que hab amos pasado ant er i or ment e / / por que l os t hr eads no se quedan con el val or si no con l a di r ecci n voi d *f unci on_t hr ( t hr _par am_t *p) { / / Est a es l a f unci on que ej ecut an l os t hr eads / / como ver as, no t i ene mucho secr et o. . . pr i nt f ( " %s %d\ n" , p- >cadena, p- >i d) ; / / una vez t er mi namos, devol vemos el val or pt hr ead_exi t ( &( p- >i d) ) ; } i nt mai n( voi d) { i nt i , *r es; / / cr eamos l os t hr eads pr i nt f ( " Cr eando t hr eads. . . \ n" ) ; f or ( i =0; i <MAX_THREADS; i ++) { par am[ i ] . cadena = st r dup( " Hol a, soy el t hr ead" ) ; par am[ i ] . i d = i ; pt hr ead_cr eat e( &t abl a_t hr [ i ] , NULL, ( voi d *) &f unci on_t hr , ( voi d *) &par am[ i ] ) ; } / / esper amos que t er mi nen l os t hr eads pr i nt f ( " Thr eads cr eados. Esper ando que t er mi nen. . . \ n" ) ; f or ( i =0; i <MAX_THREADS; i ++) { pt hr ead_j oi n( t abl a_t hr [ i ] , ( voi d *) &r es) ; pr i nt f ( " El t hr ead %d devol vi o el val or %d\ n" , i , *r es) ; } / / sacamos el mensaj i t o y sal i mos del pr ogr ama pr i nt f ( " Todos l os t hr eads f i nal i zados. Adi os! \ n" ) ; r et ur n 0; } / *** <CORTAR AQUI > ***/
Para compilar (bajo Linux y gcc): gcc ej 1. c - o ej 1 - l pt hr ead
El ejemplo en s es MUY tonto, pero es el esquema bsico que siguen todas las aplicaciones que lanzan threads para realizar un clculo y esperan su resultado: 1. Crear el/los thread(s) 2. Esperar que terminen 3. Recoger y procesar el/los resultado(s) Esto es un ejemplo de lo que se llama paralelismo estructurado. Un ejemplo de un programa que use la funcin pt hr ead_det ach podra ser el de un servidor (de cualquier cosa: de correo, de http, de ftp, etc) que cree un hilo para cada peticin que reciba. Como que no nos interesa el resultado de la ejecucin, una vez hayamos creamos el thread llamaremos la funcion pt hr ead_det ach. Esto es lo que se conoce por paralelismo no estructurado. Es decir, nuestros programas no siguen una estructura concreta sino que se van ramificando segn nuestras necesidades. 1.4. Otras funciones tiles de la biblioteca pthreads Hasta ahora hemos visto las funciones ms bsicas para tratar con pthreads, pero an queda alguna otra funcin til: pt hr ead_t pt hr ead_sel f ( voi d) Esta funcin devuelve al thread que la llama su informacin, en forma de variable del tipo pthread_t. Es til si el propio thread que se est ejecutando quiere cambiar sus atributos, hacerse l mismo un pt hr ead_det ach, etc. Devuelve el identificador del thread. Ejemplo:
#i ncl ude <pt hr ead. h> . . . voi d *f unci on_t hr eads( voi d *par am) { pt hr ead_t yo_mi smo; . . . / * nosot r os mi smos nos hacemos el det ach */ yo_mi smo = pt hr ead_sel f ( ) ; pt hr ead_det ach( yo_mi smo) ; . . . } i nt mai n( voi d) { . . . } i nt pt hr ead_ki l l ( pt hr ead_t t hr ead, i nt si gno)
Enva un signal especificada al thread especificada. Un signal til de enviar puede ser el SIGKILL, o alguno de los definidos por el usuario, SIGUSR1 y SIGUSR2. Aunque pueda parecer til a primera vista, la nica utilidad que tiene es matar un thread desde el proceso padre. Si se quiere usar con fines de sincronizacin hay formas mejores de hacerlo tratndose de threads: mediante semforos y variables de condicin (enseguida lo veremos) thread: identifica el thread al cual le queremos enviar el signal. signo: nmero de la seal que queremos enviar al thread. Podemos usar las constantes definidas en /usr/include/signal.h Devuelve 0 si no hubo error, o diferente de 0 si lo hubo.
Hasta aqu la primera parte del tutorial de programacin en Pthreads. Aqu hemos visto las funciones bsicas que nos ofrece la biblioteca para la creacin, manipulacin y eliminacin de threads, pero an nos quedan algunas cosas por ver. 2. 2. Problemas de concurrencia con Pthreads 2.1. 2.1. Introduccin 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 Esto se entender mejor con un ejemplo:
Hi l o 1 voi d *f unci on_hi l o_1( voi d *ar g) { i nt r esul t ado; . . . i f ( i == val or _cual qui er a) { . . . r esul t ado = i * ( i nt ) *ar g; . . . } pt hr ead_exi t ( &r esul t ado) ; }
Hi l o 2 voi d *f unci on_hi l o_2( voi d *ar g) { i nt ot r o_r esul t ado; . . . i f ( f unci on_sobr e_ar g( *ar g) == 0) { . . . i = *ar g; . . . } pt hr ead_exi t ( &ot r o_r esul t ado) ; }
Este cdigo, que tiene la variable 'i' como global, aparentemente es inofensivo, pero nos puede traer muchos problemas si se ejecuta en paralelo y se dan ciertas condiciones. Supongamos que el hilo 1 se empieza a ejecutar antes que el hilo 2, y que casualmente se produce un cambio de contexto (el sistema operativo suspende la tarea actual y pasa a ejecutar la siguiente) justo despus de la lnea que dice if (i==valor_cualquiera). La entrada en ese if se producir si se cumple la condicin, que suponemos que s. Pero justo en ese momento el sistema hace un cambio de contexto y pone a ejecutar al hilo2, que se ejecuta el tiempo suficiente como para ejecutar la lnea i =*arg. Al poco rato hilo 2 deja de ejecutarse y vuelve a ejecutarse el hilo 1, pero, qu valor tiene ahora i? El que el hilo 1 est "suponiendo" que tiene (o sea, el mismo que comprob al entrar en el if) o el que le ha asignado el hilo 2? La respuesta es fcil... ;-) i ha tomado el valor que le asign hilo 2, con lo que el resultado que devolver el hilo 1 despus de sus clculos ser totalmente invlido e inesperado. Claro que todo esto puede que no pasar si el sistema tuviera muy pocos procesos en ese momento (con lo cual cada proceso se ejecutara por ms rato) y si el cdigo del hilo 1 fuera lo suficientemente corto como para no sufrir ningn cambio de contexto en medio de su ejecucin... Pero NUNCA deberemos hacer suposiciones de stas, porque no sabremos dnde se van a ejecutar nuestros programas y siempre ms vale prevenir. El problema que tienen estos bugs es que son los ms difciles de detectar en el caso que no nos fijramos en que podra pasar una cosa de estas el da que escribimos el cdigo. Puede que a veces vaya todo a la perfeccin y que otras salga todo mal... A esto se le conoce por Race Conditions (o en cristiano, Condiciones de Carrera) porque segn como vaya la cosa puede funcionar o no. 2.2. 2.2. Mecanismos de Pthreads para prevenir esto La biblioteca de Pthreads nos ofrece unos mecanismos bsicos pero muy tiles para definir esto. Estos mecanismos son los llamados semforos binarios, y se usan para implementar las llamadas regiones crticas (RC) o zonas de exclusin mtua (ZE). Y qu es una RC? Pues una parte de nuestro cdigo que es susceptible de verse afectada por cosas como la del ejemplo. Como regla general, SIEMPRE que haya variables o estructuras globales que vayan a ser accedidas por ms de un thread a la vez, el acceso a stas deber ser considerado una regin crtica, y protegido con los medios que vamos a explicar a continuacin. Incluso si estamos seguros que solo un hilo va a acceder a una determinada estructura, no sera mala idea meter ese cdigo en una RC porque tal vez en un futuro ampliemos nuestro cdigo y no recordemos que tenamos esos accesos por ah escondidos, con el consiguiente riesgo de bugs que ello conlleva. Lo que Pthreads nos ofrece son los semforos binarios, semforos mutex o simplemente mutexs, como cada uno quiera llamarlo ;-) Un semforo binario es una estructura de datos que acta como un semforo porque puede tener dos estados: o abierto o cerrado. Cuando el semforo est abierto, al primer thread que pide un bloqueo se le asigna ese bloqueo y no se deja pasar a nadie ms por el semforo. Mientras que si el semforo est cerrado, porque algn thread ya tiene el bloqueo, el thread que lo pidi parar su ejecucin hasta que no sea liberado el susodicho bloqueo. Solo puede haber un solo thread poseyendo el bloqueo del semforo, mientras que puede haber ms de un thread esperando para entrar en la RC, encolados en la cola de espera del semforo. Es decir, los threads se excluyen mtuamente (de ah lo de mutex para el nombre) el uno al otro para entrar. Pues con una cosa tan sencilla en concepto se implementan las RC: se pide el bloqueo del semforo antes de entrar, ste es otorgado al primero que llega, mientras que los dems se quedan bloqueados esperando a que el que entr primero libere el bloqueo o exclusin. Una vez el que entr sale de la RC, ste debe notificarlo a la biblioteca de pthreads para que mire si haba algn otro thread esperando para entrar en la cola. Si lo haba, le da el bloqueo al primero y deja que siga ejecutndose. Las funciones que ofrece Pthreads para llevar esto a cabo son:
i nt pt hr ead_mut ex_i ni t ( pt hr ead_mut ex_t *mut ex, const pt hr ead_mut exat t r _t *at t r )
Esta funcin inicializa un mutex. Hay que llamarla antes de usar cualquiera de las funciones que trabajan con mutex. mutex: Es un puntero a un parmetro del tipo pt hr ead_mut ex_t , que es el tipo de datos que usa la biblioteca Pthreads para controlar los mutex. attr: Es un puntero a una estructura del tipo pt hr ead_mut exat t r _t , y sirve para definir qu tipo de mutex queremos: normal, recursivo o errorcheck (esto se ver ms adelante) Si este valor es NULL (recomendado), la biblioteca le asignar un valor por defecto. La funcin devuelve 0 si se pudo crear el mutex o -1 si hubo algn error.
i nt pt hr ead_mut ex_l ock( pt hr ead_mut ex_t *mut ex) Esta funcin pide el bloqueo para entrar en una RC. Si queremos implementar una RC, todos los thread tendrn que pedir el bloqueo sobre el mismo semforo. mutex: Es un puntero al mutex sobre el cual queremos pedir el bloqueo o sobre el que nos bloquearemos en caso de que ya haya alguien dentro de la RC. Como resultado, devuelve 0 si no hubo error, o diferente de 0 si lo hubo.
i nt pt hr ead_mut ex_unl ock( pt hr ead_mut ex_t *mut ex) Esta es la funcin contraria a la anterior. Libera el bloqueo que tuviramos sobre un semforo. mutex: Es el semforo donde tenemos el bloqueo y queremos liberarlo. Retorna 0 como resultado si no hubo error o diferente de 0 si lo hubo.
i nt pt hr ead_mut ex_dest r oy( pt hr ead_mut ex_t *mut ex) Le dice a la biblioteca que el mutex que le estamos indicando no lo vamos a usar ms, y que puede liberar toda la memoria ocupada en sus estructuras internas por ese mutex. mutex: El mutex que queremos destruir. La funcin, como siempre, devuelve 0 si no hubo error, o distinto de 0 si lo hubo.
Estas son las funciones ms bsicas. Ahora, reescribiremos el pseudocdigo del ejemplo anterior con lo que hemos visto hasta ahora.
Var i abl es gl obal es:
pt hr ead_mut ex_t mut ex_acceso; i nt i ;
Hi l o 1 ( Ver si n cor r ect a) voi d *f unci on_hi l o_1( voi d *ar g) { i nt r esul t ado; . . .
pt hr ead_mut ex_l ock( &mut ex_acceso) ; i f ( i == val or _cual qui er a) { . . . < r esul t ado = i * ( i nt ) *ar g; . . . < }
Hi l o 2 ( Ver si n cor r ect a) voi d *f unci on_hi l o_2( voi d *ar g) { i nt ot r o_r esul t ado; . . . i f ( f unci on_sobr e_ar g( *ar g) == 0) { . . .
i nt mai n( voi d) { . . . pt hr ead_mut ex_i ni t ( &mut ex_acceso, NULL) ; . . . }
En color azul han sido aadidas las lneas que antes no estaban. Como se puede ver lo nico que hay que hacer es inicializar el semforo, pedir el bloqueo antes de las RC y liberarlo despus de salir de la RC, aunque a veces es cuestin tambin de tener un poco de vista. Contra ms pequeas hagamos las RC, ms concurrentes sern nuestros programas, porque tendrn que esperar menos tiempo en el caso de que haya bloqueos. 2.3. 2.3. Problemas... Ms problemas? Aunque esto realmente soluciona el problema de los accesos concurrentes, tambin nos puede traer ms problemas. Y los problemas aqu tambin tienen nombre: los Deadlocks (o Abrazos Mortales) Los Deadlocks se producen cuando un hilo se bloquea esperando un recurso que tiene bloqueado otro hilo que est esperando un recurso. Si el recurso para el segundo thread no llega nunca, no se desbloquear nunca, con lo cual tampoco se desbloquear nunca el primer thread. Resultado: nuestro fantstico programa bloqueado. Solucin? Pues aunque la biblioteca de Pthreads nos de algn mecanismo para intentar prevenir que esto se produzca, no hay ningn mecanismo fiable al 100% para prevenirlos. El modelo ms sencillo de Deadlock es el circular:
Hi l o 1 voi d *f unci on_hi l o_1( voi d *ar g) { . . . pt hr ead_mut ex_l ock( &mut ex_1) ; . . .
pt hr ead_mut ex_unl ock( &mut ex_2) ; . . . }
Hi l o 2 voi d *f unci on_hi l o_2( voi d *ar g) { . . . pt hr ead_mut ex_l ock( &mut ex_2) ; . . .
pt hr ead_mut ex_unl ock( &mut ex_1) ; . . . }
Parece un poco raro, pero puede llegarse a producir. Mecanismos que ofrece la biblioteca Pthreads:
1. Semforos recursivos Estos semforos solo aceptarn una sola peticin de bloqueo por el mismo thread. Con los semforos normales, si el mismo thread hace 10 llamadas a pt hr ead_mut ex_l ock sobre el mismo semforo, luego tendr que hacer 10 llamadas a pt hr ead_mut ex_unl ock, es decir, tantas como haya hecho a pt hr ead_mut ex_l ock. En cambio, los del tipo recursivo solo aceptarn una sola llamada a pt hr ead_mut ex_l ock. Las siguientes llamadas sern ignoradas, con lo que ya eliminamos un tipo de deadlock. Para poder crear un semforo recursivo, tendremos que decrselo a pt hr ead_mut ex_i ni t , indicndole como atributo el resultado de una llamada a pt hr ead_mut exat t r _set t ype. El procedimiento es: Definir una variable del tipo pt hr ead_mut exat t r _t : pt hr ead_mut exat t r _t mut ex_at t r ; Inicializarla con la llamada a pht r ead_mut exat t r _i ni t : pt hr ead_mut exat t r _i ni t ( &mut ex_at t r ) ; Indicarle el tipo explcitamente mediante pt hr ead_mut exat t r _set t ype: pt hr ead_mut exat t r _set t ype( &mut ex_at t r , t i po) ; Donde tipo puede ser PTHREAD_MUTEX_NORMAL, PTHREAD_MUTEX_DEFAULT (el que se usa por defecto), PTHREAD_MUTEX_RECURSI VE o PTHREAD_MUTEX_ERRORCHECK.
2. Probar antes de entrar Si creemos que la siguiente llamada a pt hr ead_mut ex_l ock va a ser bloqueante y que puede provocar un deadlock, la biblioteca de Pthreads nos ofrece una funcin ms para comprobar si eso es cierto: pt hr ead_mut ex_t r yl ock. i nt pt hr ead_mut ex_t r yl ock( pt hr ead_mut ex_t *mut ex) ; mutex: Es el mutex sobre el cual queremos realizar la prueba de bloqueo. La funcin devuelve EBUSY si el thread llamante se bloquear o 0 en caso contrario. Si no se produce el bloqueo, la funcin acta igual que phtread_mutex_lock, adquiriendo el bloqueo sobre el semforo.
3. Funciones avanzadas Otras funciones de uso avanzado con pthreads son las siguientes:
int pt hr ead_at t r _i ni t ( pt hr ead_at t r _t *at t r ) ;
i nt pt hr ead_at t r _dest r oy( pt hr ead_at t r _t *at t r ) ;
i nt pt hr ead_at t r _set det achst at e( pt hr ead_at t r _t *at t r , i nt det achst at e) ;
i nt pt hr ead_at t r _get det achst at e( const pt hr ead_at t r _t *at t r , i nt *det achst at e) ;
i nt pt hr ead_at t r _set schedpol i cy( pt hr ead_at t r _t *at t r , i nt pol i cy) ;
i nt pt hr ead_at t r _get schedpol i cy( const pt hr ead_at t r _t *at t r , i nt *pol i cy) ;
i nt pt hr ead_at t r _set schedpar am( pt hr ead_at t r _t *at t r , const st r uct sched_par am*par am) ;
i nt pt hr ead_at t r _get schedpar am( const pt hr ead_at t r _t *at t r , st r uct sched_par am*par am) ;
Su utilizacin va ms all de los objetivos del curso y su estudio se deja al alumno