Sunteți pe pagina 1din 13

Lenguajes de Programacin I

Evolucin de la Pila de Registros de Activacin en Lenguajes tipo ALGOL con unidades concurrentes
Los lenguajes tipo ALGOL son tambin llamados orientados a pila. La memoria se usara de esta forma: Programa Ejecutable Datos La Frontera vara en cada instante de la Ejecucin

Estos lenguajes tienen bloques ya sea sin nombre o con nombre (funciones). Dentro de los bloques pueden definirse variables para emplear. Al llamar a estos bloques, en la pila de ejecucin se crean los registros de activacin en donde se almacenan las variables que se emplean. Como se vio en prcticas anteriores, el registro de Activacin es el conjunto de todos los elementos que necesita un bloque para ejecutarse. Variables Direccin de Retorno si llamo a una funcin (cadena dinmica) Cadena Esttica: Puntero al padre en el rbol de anidamiento.

Por ejemplo, si tenemos la siguiente invocacin de funciones: ABBCD

La pila de ejecucin con los registros de activacin se veran de la siguiente forma: RA de D

RA de C

RA de B

RA de B

RA de A

Cdigo

Datos

En el caso de que el lenguaje permita el uso de unidades concurrentes, cada una de estas unidades tiene su propia pila. Tambin es importante aclarar que comparten la seccin de cdigo y datos con los dems threads ya que forman parte de un mismo proceso. La vista interna de las pilas de Registros de Activacin se vera de la siguiente forma: RA de D

RA de C

RA de B

RA de B

RA de A

Thread Principal Cdigo

Thread1 Datos

Thread2

..

Thread n

l Pgina 2

Es importante aclarar que las pilas y sus tamaos varan en forma independiente como lo muestra el dibujo. A continuacin se procede a explicar la resolucin de los ejercicios de la prctica. Ejercicio 1 Cdigo compilable #include <pthread.h> #include <stdio.h> void *XX(void) { int i; printf("%x\n",&i); } int main () { pthread_t thread; pthread_create(&thread, NULL, XX, NULL); XX(); XX(); XX(); pthread_exit(NULL); } Si tenemos en cuenta lo expuesto al principio, podriamos suponer la siguiente situacin en lo que se refiere a registros de activacin: Primer llamada desde el thread main a XX():

XX() i bfaba8d4

Thread Principal

Thread 1

La segunda y la tercer llamada son exactamente iguales ya que al no llamar XX a ningn bloque, se apila y desapila el registro de activacin en el mismo espacio de direcciones, es por ello que tres de las direcciones de memoria de la salida deberan coincidir. La pila de RA del thread creado por thread_create (podramos llamarlo thread1 para diferenciarlo del thread principal) se vera de la siguiente forma:

l Pgina 3

XX() i b7e1d3c 4 Thread Principal Thread 1

Al ser distintas pilas resulta natural que las direcciones de memoria de la variable i difieran en el caso del thread principal y del thread creado por pthread_create y que la direccin de i en las tres llamadas desde el thread principal sea la misma. Esto nos lleva a decir que las respuestas vlidas pueden ser a) o la b) Si comprobamos estas salidas compilando y ejecutando el programa podemos ver que La salida del programa es la siguiente:

Si introducimos una demora en la funcin XX: #include <pthread.h> #include <stdio.h> void *XX(void) { int i; for (i=0; i<999999999; i++); //Demora para imprimir. printf("%x\n",&i); } int main () { pthread_t thread; pthread_create(&thread, NULL, XX, NULL);

l Pgina 4

XX(); XX(); XX(); pthread_exit(NULL); } Nos da tiempo para que las 3 ejecuciones del thread del main puedan alternarse con la del thread creado con la funcin pthread_create:

Los grficos de la pila de registros de activacin no difieren del caso anterior (son el delay introducido). Se comprueba entonces que las salidas vlidas son la a) o la b).

l Pgina 5

Ejercicio 2 Cdigo compilable #include <pthread.h> #include <stdio.h> void *mensaje(void* msj) { printf("%s\n",msj); } int main () { pthread_t threadHola,threadMundo; pthread_create(&threadHola, NULL, mensaje, "Hola"); pthread_create(&threadMundo, NULL, mensaje, "Mundo"); pthread_exit(NULL); } El programa anterior crea dos threads uno que imprime Hola y el otro Mundo. Al ser threads independientes, no podemos asegurar cual va a ejecutarse primero, con lo cual la salida puede ser tanto Hola Mundo como Mundo Hola. No obstante, si compilamos y ejecutamos el cdigo anterior, simplemente por el orden de creacin y por el escaso tiempo que lleva ejecutar cada uno de los llamados a la funcin mensaje, es posible que el resultado que veamos sea siempre Hola Mundo Si introducimos una demora en la impresin del mensaje, por ejemplo: #include <pthread.h> #include <stdio.h> void *mensaje(void* msj) { int i; for (i=0; i<999999999; i++); //Demora introducida printf("%s\n",msj); } int main () { pthread_t threadHola,threadMundo; pthread_create(&threadHola, NULL, mensaje, "Hola"); pthread_create(&threadMundo, NULL, mensaje, "Mundo"); pthread_exit(NULL); } La salida puede alternarse como lo muestra la siguiente pantalla:

l Pgina 6

Ejercicio 3 Cdigo compilable #include <pthread.h> #include <stdio.h> void *XX(void) { static int i; printf("%x\n",&i); } int main () { pthread_t thread; pthread_create(&thread, NULL, XX, NULL); XX(); XX(); XX(); pthread_exit(NULL); } Las variables estticas se crean antes de la ejecucin, su tiempo de vida se extiende a lo largo de toda la ejecucin del programa, pero el mbito queda limitado a la unidad en la cual se ha declarado. En C, las variables pueden llegar a ser explcitamente estticas (por medio de la clusula static). La salida del programa puede verse en la siguiente pantalla:

Las direcciones de memoria coinciden en todos los casos. El motivo se debe a que tanto las variables globales como las estticas son compartidas por todos los threads.

Ejercicio 4 Cdigo compilable #include <pthread.h> #include <stdio.h> #define NUM_THREADS 5 void *imprimir(void *threadid) { int j; printf("Thread %d trabajando......\n",threadid); for (j=0; j<99999999; j++); printf("Thread %d termino\n",threadid); } int main (int argc, char *argv[]) {

l Pgina 7

pthread_t threads[NUM_THREADS]; int rc, t; for(t=0; t<NUM_THREADS; t++){ printf("Creando thread %d\n", t); pthread_create(&threads[t], NULL, imprimir, (void *)t); } pthread_exit(NULL);

La salida del programa puede verse a continuacin:

Donde, si bien cada llamada a imprimir ocupa el procesador prcticamente al 100%, los threads siguen ejecutndose e incluso terminan en distinto orden. Ahora bien, cambiemos el cdigo anterior por el siguiente, resulta natural plantear que al desconocer el SO la presencia de threads, si el programa realiza una llamada al sistema que bloquea al proceso, el scheduler del SO tomar otro proceso para ejecutar, durmiendo al proceso completo y por ende a todos sus threads. #include <pthread.h> #include <stdio.h> #define NUM_THREADS 5 void *imprimir(void *threadid) { int j; printf("Thread %d por tomarse un descanso......\n", threadid); //LLamada que bloquee al proceso (todos los threads quedarian bloqueados ya que el sistema trabaja a Nivel proceso, no thread block(); printf("Thread %d termino\n",threadid); } int main (int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; int rc, t; for(t=0; t<NUM_THREADS; t++){ printf("Creando thread %d\n", t); pthread_create(&threads[t], NULL, imprimir, (void *)t); } pthread_exit(NULL); }

l Pgina 8

Ejercicio 5 Cdigo compilable #include <pthread.h> #include <stdio.h> #define NUM_THREADS 5 void *imprimir(void *threadid) { static __thread int j; printf("Thread: %d Direccion de j: %x\n",threadid,&j); } int main (int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; int rc, t; for(t=0; t<NUM_THREADS; t++){ printf("Creando thread %d\n", t); pthread_create(&threads[t], NULL, imprimir, (void *)t); } pthread_exit(NULL); }

Eliminando __thread de la declaracin de y:

En el caso que no se incluye __thread en la definicin static, vemos que la variable es la misma para todos los threads. Al emplear __thread, la variable pasa a ser local en cada thread.

Material Adicionales
Ejercicio Avanzado #include <pthread.h>

l Pgina 9

#include <stdio.h> #define NUM_THREADS 2 #define NUM_ITERACIONES 3 int var_global=0; void imprimir_desde_thread(void *threadid, int count) { static int v=0; int i=0; int j; if (count<NUM_ITERACIONES){ for (j=0; j<99999999; j++); count++; printf("Thread '%d' - Posicion i en memoria '%x' Valor i '%d' - Iteracion: %d\n",threadid, &i,i,count); printf("Thread '%d' - Distancia de i a v: %x\n", threadid,&i-&v); imprimir_desde_thread(threadid,count); }else{ printf("-------------------\n"); printf("Thread '%d' - Acceso a Valor Global '%d' Mem Global '%x'\n", threadid,var_global++,&var_global); printf("-------------------\n"); } } void *imprimir(void *threadid){ imprimir_desde_thread(threadid,0); } int main (int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; int rc, t; for(t=0; t<NUM_THREADS; t++){ printf("Creando thread %d\n", t); rc = pthread_create(&threads[t],NULL,imprimir,(void *)t); if (rc){ printf("ERROR; El pthread_create() retorno %d\n", rc); exit(-1); } } imprimir((void *)NUM_THREADS); pthread_exit(NULL); }

l Pgina 10

Threads en Java El tamao de la pila de los threads puede limitarse por medio de parmetros a la Virtual Machina: -Xms y Xmx Tamao del heap -Xss tamao del STACK Por ejemplo: java prueba Xms256M Xms256M .Xss248K Ejecutara la clase prueba con un heap de 256 MB (mnimo y mximo coinciden) y con un tamao permitido para las pilas de los threads de 248 KB. Fork: Creacin de procesos en C Si se planteara el caso, podra compararse la ejecucin de mltiples threads en un lenguaje estilo C contra la creacin de mltiples procesos. El ejemplo que se muestra a continuacin crea dos procesos independientes. Se muestra mediante el comando top de unix la ejecucin simultnea de ambos y tambin la salida. #include <stdio.h> int main () { int fork_return; int global = 0; int count = 0;

l Pgina 11

printf("Proceso %d est por crear un hijo.\n", getpid() ); fork_return = fork(); printf(Desde ambos procesos!!!!); if( fork_return > 0){ int j; printf("Proceso padre %d.\n", fork_return); global++; printf("Valor y Direccion de Memoria de global en a: %d , %x\n",global,&global); while( count++ < 25){ for (j=0; j<99999999; j++); //sleep(5); int a=0; printf("Direccion de Memoria de a:%x\n",&a); } }else{ int j; printf("Proceso hijo %d.\n", fork_return); global++; printf("Valor y Direccion de Memoria de global en b: %d , %x\n",global,&global); while(count++ < 25){ for (j=0; j<99999999; j++); //sleep(5); int b=0; printf("Direccion de Memoria de b:%x\n",&b); } } } La comunicacin entre procesos no es tan simple como en el caso de los threads. Puede observarse que una variable externa a los bloques del padre y del proceso hijo (con el nombre global), en realidad no es comn a ambos bloques y esto tiene sentido al ser procesos separados. Esto queda comprobado al incrementar el valor de esta variable y obtener el mismo resultado en ambas ejecuciones. En el caso de los threads, una suma a una variable compartida da origen a una competencia por el uso de la misma. Se muestra a continuacin las pantallas de salida donde se ven los procesos ejecutndose bajo el nombre 02_fork

l Pgina 12

l Pgina 13

S-ar putea să vă placă și