Aplicaciones Multihilo

Threads

faq.utnso.com/threads

Que es un thread?

Vamos un poco más arriba y veamos que es una aplicación… por ej: Super Mario Bros?

Que es un thread?

Y qué pasa si tenemos MUCHAS cosas para mover en pantalla?

Para qué queremos un Thread?

Dentro del espacio de ejecución de un proceso, los threads funcionan de manera “simultánea”

Todos los threads comparten la información del proceso por encontrarse dentro del mismo. El stack y los registros NO SE COMPARTEN

int a = 0;

a = a + 1

printf(“%d”, a);

Situaciones como ésta pueden llevar a una ejecución NO determinística.

Proceso

Proceso

int a = 0;

while(a < 1000) {

a = a + 1

printf(“%d”, a);

}

a = a + 1

printf(“%d”, a);

Entonces… Qué es un thread?

Los Threads comparten una serie de recursos tales como el espacio de memoria, los archivos abiertos, el espacio de direcciones, etc ... Aun así cada Thread tiene su propio Stack por lo que esta información no puede ser compartida entre los threads

Thread

Thread

Thread

Variables globales, Archivos Abiertos, etc

Los threads comparten varios recursos, Variables globales, Archivos abiertos, Espacio de direcciones, es por esto que se pueden dar casos de condición de carrera (esto se explica en slides posteriores)

Que NO es un Thread?

Un thread NO es un programa independiente, con lo cual no es algo que se ejecute aislado. Puede interferir con otros threads del mismo proceso!

Por lo que comente en el slide anterior, los threads no pueden vivir independientemente sin alterarse, se los tiene que sincronizar, si no se lo hace es muy posible que interfieran con otros threads del mismo proceso.

Cuándo usar multithreading?

El proceso tiene que cumplir con varias funciones en simultáneo y es mucho más simple comunicar threads que procesos ya que tienen el mismo espacio de direcciones y por ende comparten variables, recursos, etc.

El titulo tambien podria ser Multithreading vs MultiProgramación vean cual les gusta mas :)

Cómo crear un thread?

Creación: 

int pthread_create(pthread_t * thread_id,
                   const pthread_attr_t * attr,
                   void * (*start_routine)(void *),
                   void * arg);

 

(Esperar a la) Terminación: 

int pthread_join(pthread_t thread_id, void **value_ptr);

Ante cualquier duda consulte a man

#include <pthread.h>

Threads – Ejemplo 4/6

void *sumarMilVeces( void *argumento ){

char *nombre = (char *) argumento;

int i = 0;

for (i; i<1000; i++) {

compartida = compartida + 1;

printf("%s : %d\n",nombre,compartida);

sleep(1);

}

}

Threads – Ejemplo

#include <pthread.h>

#include <stdio.h>

void *sumarMilVeces( void *ptr );

int compartida = 5;

int main(){

pthread_t hilo1, hilo2;

char *arg1 = "hilo1: ";

char *arg2 = "hilo2: ";

int r1, r2;

r1 = pthread_create( &hilo1, NULL, sumarMilVeces, (void*) arg1);

r2 = pthread_create( &hilo2, NULL, sumarMilVeces, (void*) arg2);

pthread_join( hilo2, NULL);

pthread_join( hilo1, NULL);

printf("Thread 1 devolvió: %d y el Thread 2: %d\n", r1, r2);

}

Threads – Ejecución

hilo2: 8

hilo1: 9

hilo2: 10

hilo1: 11

hilo1: 12

hilo2: 13

hilo1: 14

hilo2: 15

hilo1: 16

hilo2: 16

hilo2: 17

hilo1: 17

hilo1: 18

Noten cómo el procesador planifica estos hilos según va teniendo disponibilidad.

Nada garantiza que los hilos se ejecuten secuencialmente.

Situaciones anómalas se pueden producir al no proteger correctamente la sección crítica

Condición de Carrera

La condición de carrera se da cuando el resultado depende del orden de ejecución de los threads (Aplica también para procesos).

La condición de carrera es uno de los peores inconvenientes que se pueden tener a la hora de trabajar con threads ya que si no se sincronizan de manera correcta puede darse el caso de que se afecten mutuamente y en el mejor de los casos generan un seg fault, en el peor de los casos generan resultados inesperados y que van a variar aleatoriamente de acuerdo al orden en el que se ejecuten los threads, es por esto que las condiciones de carrera pueden ser uno de los peores problemas que uno puede tener con los threads.

Helgrind puede ser una gran ayuda para esto.

Helgrind!

Helgrind es una herramienta de la familia de valgrind que detecta errores (y horrores) en el manejo de threads.

Llamando a helgrind desde consola:

valgrind --tool=helgrind ./<Programa>

Hoy te convertis en heroe

Reventamos el código?

Vamo a Sincronizarno

Sincronización?

Los thread se deben sincronizar para evitar que se produzcan resultados inesperados...

Un Semáforo es un:

Tipo

Abstracto de

Datos

http://es.wikipedia.org/wiki/Tipo_de_dato_abstracto

Threads – Semáforos

...

int compartida = 5;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *sumarMilVeces( void *argumento ){

...

pthread_mutex_lock( &mutex );

for (i; i<1000; i++) {

compartida = compartida + 1;

...

}

pthread_mutex_unlock( &mutex );

}

http://faq.utn.so/semaforos

Operaciones atómicas

Post(sem){

if(sem < 0)

despertar();

sem += 1;

}

Wait(sem){

sem -= 1;

if (sem < 0 )

blockear();

}

El código es a modo de ejemplo.

Estas son System calls, no hay forma de asegurar que sea así, pero hace lo que esta aca escrito; líneas más, líneas menos.

Threads – Semáforos - Ej

void cambiarValor(){

while(a < 20){


int random = (rand() % 100);


variable = random;


printf("\nThread2: %d", variable);


a++;


}
}

http://faq.utn.so/semaforos

Threads – Semáforos - Ej

int main(void) {
srand((unsigned) time(&tiempo));
pthread_t thr1;
variable = 0;
pthread_create(&thr1, NULL, (void *)cambiaValor, NULL);
int a = 0;

while (a < 20){
variable = 30;
printf("\nThread Main: %d", variable);
a++;
}


return EXIT_SUCCESS;
}

http://faq.utn.so/semaforos

  • pthread_mutex_init()
  • pthread_mutex_lock()
  • pthread_mutex_unlock()
  • pthread_mutex_destroy()
  • sem_init()
  • sem_wait()
  • sem_post()
  • sem_destroy()

pthread hay que linkearlo explícitamente en el compilador.

Compilación

¿Como compilamos con la biblioteca Pthread?

gcc thread.c -lpthread

Si agregamos la biblioteca en eclipse ya nos queda en el make y no hay que hacer mas nada

pthread hay que linkearlo explícitamente en el compilador.

Pool de Threads vs Threads on Demand

Un Pool de Threads es un conjunto de threads que se crean una única vez y pueden ser re-utilizados a lo largo de la ejecución del proceso

Un thread bajo demanda es aquel que se crea cuando es requerido y que al finalizar su ejecución el mismo debe terminar

Threads Detachables

Los threads si se crean bajo demanda no sabemos cuantos se van a crear, asique podemos hacer que liberen sus recursos cuando terminan.

¿Preguntas?

FIN

Linux API - Presentación Threads Merge + New Stuff - Google Slides