1. Sistemas Distribuidos
Proceso y Comunicación con
Hilos
René Guamán-Quinche
Facultad de la Energía, las Industrias y los Recursos Naturales No Renovables
Carrera de Ingeniería en Sistemas/Computación
Noviembre, 2020
Loja, Ecuador
3. 3
Objetivo
Comprender los diferentes conceptos relacionados con los procesos y la
comunicación distribuidos como la base para el desarrollo de
aplicaciones distribuidas.
4. 4
Proceso
Programa: fichero con comandos (Estáticos)
Proceso: Abstracción de un programa en ejecución (Dinámico)
La CPU es un recurso importantísimo
Mas procesos que CPUs → ¿Qué procesos la usa?
Ocurre igual con otros recurso ilimitados (ej. memoria)
Multiprogramación: La CPU pasa de un proceso a otro rápidamente dándole a
cada uno un pequeño tiempo de computación
→ pseudo-paralelismo
→ cambio de contexto
→ algoritmos de planificación
Multiprocesador → Verdadero paralelismo
5. 5
Proceso
Definición
Programa en ejecución
Unidad de procesamiento gestionada por el SO
Para que un programa pueda ser ejecutado ha de residir en memoria principal
Árbol de procesos
Relación de jerarquías
6. 6
Proceso
El sistema operativo mantiene un conjunto de estructuras de información que
permiten identificar y gestionar los procesos
7. 7
Proceso monothread o multithread
Un SO de multiprocesamiento se puede ejecutar varios procesos al mismo tiempo
Cada proceso se caracteriza por ser autónomo y una identidad indenpendiente y
tienen su propio espacio de memoria
SO tiene un scheduler o planificador, el que decide cual proceso puede utilizar la CPU
en un momento dado
En sistemas uni-procesador solo un proceso puede a la vez
Una característica de los procesos es que no comparten nada, son procesos separados
shared nothing
Debe haber servicios en el SO que permitan compartir memoria
Aplicaciones múltiples hilos tiene varios puntos de ejecución o varios caminos o rutas en
las cuales se pueden ejecutar las aplicaciones
Cada puntos de ejecución es llamado hilo
Los hilos comparten memoria
8. 8
Proceso monothread o multithread
Existen aplicaciones de un sólo hilo o hilo de ejecución
Si una aplicación tiene muchas tareas por ejecutar, solo una tarea se puede ejecutar a la
vez
La tareas requieren mucho procesamiento puede parecer que la aplicación se ha
congelado o no responde
Aplicaciones Multitheaded cada tarea esta ejecutada por un hilo
Permite que las aplicaciones sean más modulares donde cada tarea está en una tarea, y
cada modulo está ejecudo por un hilo
Ejemplos:
Editor de documentos
Manejador de ventanas
9. 9
Proceso monothread o multithread
Un proceso ligero, hilo o thread es un programa en ejecución (flujo de ejecución) que
comparte la imagen de memoria (y otras informaciones) con otros procesos ligeros
Dependiendo del número de procesos que puedan ejecutar simultáneamente, un SO es
monotarea o multitarea
Un proceso ligero puede contener un solo flujo de ejecución (monothread) o más
(multithread)
Por ejemplo, en el lenguaje C el hilo de ejecución primario se corresponde con la
función principal main
10. 10
Hilo
Información propia/compartida
Por thread (información propia)
Contador de programa, Registros
Pila
Estado (ejecutando, listo o bloqueado)
Por proceso (información compartida)
Espacio de memoria
Variables globales
Ficheros abiertos
Procesos hijos
Temporizadores
Señales y semáforos
Contabilidad
12. 12
Hilo
Estructura de un proceso con threads
Cada hilo tiene su propio contexto, ej.
registros, y las llamadas a pila
Quien selecciona el siguiente hilo a
ejecutar
SO scheduleder
Procesos de usuario a ejecutar
Los programadores no pueden hacer
ninguna presunción respecto a cómo
los hilos son planificados, es aleatorio
Los SO, plataformas tienen sus
propias políticas de planificación
13. 13
Hilo
Como los procesos convencionales, un proceso ligero puede estar en varias situaciones o
estados: ejecutando, listo o bloqueado
El estado del proceso será la combinación de los estados de sus procesos ligeros
Estados del proceso ligero
14. 14
Hilo
Threads y paralelismo
Los procesos ligeros permiten paralelizar una aplicación
Un programa puede dividirse en procedimientos que pueden ejecutar de manera
independiente mediante procesos ligeros
Los procesos ligeros pueden lanzar su ejecución de manera simultánea
Diseño modular de la aplicación
La base del paralelismo:
Mantener algún proceso ligero siempre en estado de ejecución: “mientras que un proceso
está bloqueado otro podría estar ejecutando”
17. 17
Posix
Posix Threds
Posix – Portable Operating System Interface
Define rutinas
Creación
Barrera sobre los hilos, puntos de sincronización
Otros
Matar
Inpeccionar características de los hilos
18. 18
Posix
Pthreads es la implementacipon de Posix thereads
Linux, Mac y Windows
Todos los programas que usen pthreads requiren incluir el archivo de
cabecera pthread.h
Para compiar un programa en phtread
gcc pthread demo.c -o demo
19. 19
Posix
Crear un thread:
int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void * (*funcion)(void *),
void * arg);
donde:
thread Identificador del nuevo thread (se captura cuando se crea)
attr Atributos a aplicar al nuevo thread o NULL
funcion Función a ejecutar por el thread que es creado
arg Puntero a el/los argumentos del thread
devuelve:
int 0 si éxito o -1 si error
Obtener el identificador de un thread:
pthread_t pthread_self(void);
20. 20
Posix
Atributos de un proceso ligero:
Un hilo se crea con una serie de atributos por defecto
Ejemplo: Por defecto, un hilo es joinable o No Independiente
Es posible modificar los atributos por defecto del hilo:
Inicia un objeto atributo que se puede utilizar para crear nuevos procesos ligeros
int pthread_attr_init(pthread_attr_t *attr);
Destruye un objeto de tipo atributo
int pthread_attr_destroy(pthread_attr_t *attr);
Cambiar el estado de terminación (Joinable / Detached)
int pthread_attr_setdetachstate( pthread_attr_t *a, int detachstate);
Si detachstate es
PTHREAD_CREATE_DETACHED el proceso se crea independiente
PTHREAD_CREATE_JOINABLE el proceso se crea no independiente
Otras atributos: tamaño de pila, planificación, prioridad, etc.
21. 21
Posix
Terminación de procesos ligeros
Finaliza la ejecución del proceso ligero:
int pthread_exit(void *value);
Para procesos creados como NO independientes es además necesario:
int pthread_join(pthread_t thid, void **value);
pthread_t: identificador del hilo que se espere que termine
**value: se va almacenar el resultado de algunos valores que devuelva el hilo al
terminar
Espera la terminación del proceso con identificador thid
Devuelve en el segundo argumento el valor pasado en pthread_exit.
Sólo se puede esperar la terminación de procesos ligeros no
independientes (PTHREAD_CREATE_JOINABLE)
22. 22
Ejemplos
Práctica 1: Threads
#include <pthread.h>
#include <stdio.h>
#define MAX 100
void *inc_x(void *x_void_ptr);
int main(){
int x = 0;
int y = 0;
printf("x: %d, y: %dn", x, y);
pthread_t inc_x_thread;
if (pthread_create(&inc_x_thread, NULL, inc_x, &x)){
fprintf(stderr, "Error creating threadn");
return 2;
}
while (++y <MAX);
printf("y increment finishedn");
printf("X: %d, y: %dn", x, y);
return 0;
}
void *inc_x(void *x_void_ptr){
printf("I am in methodn");
int *x_ptr = (int *)x_void_ptr;
while (++(*x_ptr) < MAX);
printf("x increment finishedn");
return NULL;
}
hilo1_posix.cpp
24. 24
Ejemplos
Práctica 1: Threads
¿Porqué x = 0?
Porque en el primer ejemplo el programa no espera el hilo que termine su ejecución
¿Cómo hacemos que programa espere al hilo?
25. 25
Ejemplos
Práctica 2: Threads hilo2_posix.cpp
Agregar la función pthread_join para que el programa espere hasta que hilo
termine su ejecución
if(pthread_join(inc_x_thread, NULL)){
fprintf(stderr, "Error joining threadn");
return 2;
}
26. 26
Ejemplos
Práctica 2: Threads hilo2_posix.cpp
#include <pthread.h>
#include <stdio.h>
#define MAX 100
void *inc_x(void *x_void_ptr);
int main(){
int x = 0;
int y = 0;
printf("x: %d, y: %dn", x, y);
pthread_t inc_x_thread;
if (pthread_create(&inc_x_thread, NULL, inc_x,
&x)){
fprintf(stderr, "Error creating threadn");
return 2;
}
while (++y <MAX);
printf("y increment finishedn");
// esperamos al a todos los hilos
if(pthread_join(inc_x_thread,NULL)){
fprintf(stderr, "Error joining
threadn");
return 2;
}
printf("X: %d, y: %dn", x, y);
return 0;
}
void *inc_x(void *x_void_ptr){
printf("I am in methodn");
int *x_ptr = (int *)x_void_ptr;
while (++(*x_ptr) < MAX);
printf("x increment finishedn");
return NULL;
}
27. 27
Ejemplos
Práctica 2: Threads hilo2_posix.cpp
El tiempo de ejecución incrementa porque el programa ahora espera al hilo hasta que termine
28. 28
Hilos y concurrencia
Se tiene un vector de 15 elementos, se quiere buscar cuantas veces se repite 3
La complejidad del problema es O(n), porque se a ir comparando elemento por
elemento y si se encontra un número 3 se va incrementando un contador
for (i = 0; i > len(vector); i++)
if (vector [ i ] == 3)
count++
29. 29
Hilos y concurrencia
Threads and concurrency programa.cpp – hilo secuencial
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define VECTOR_SIZE 100000000
// variables globales
int *array;
int count = 0;
int double_count = 0;
void initialize_vector(){
int i = 0;
array = (int*) malloc(sizeof(int) * VECTOR_SIZE);
if(array == NULL){
printf("Memoria no disponiblen");
exit(-1);
}
for(;i < VECTOR_SIZE; i++){
array[i] = rand() % 20;
if (array[i] == 3)
double_count++;
}
}
void count_3s(){
int i = 0;
for(;i < VECTOR_SIZE; i++){
if (array[i] == 3)
count++;
}
}
30. 30
Hilos y concurrencia
Threads and concurrency programa.cpp – hilo secuencial
int main(int argc, char* argv[]){
int i = 0;
int err;
clock_t t1, t2;
printf("Running 3s-00");
srand(time(NULL));
printf("*** 3s-00 ***n");
printf("Initializing vector.... ");
fflush(stdout);
initialize_vector();
printf("Vector initialized! n");
fflush(stdout);
t1 = clock();
count_3s();
t2 = clock();
printf("Count by thread %d!n",count);
printf("Double check %d!n",double_count);
printf("[[3s-00] tiempo transcurrido %fn",(((float)t2 - (float)t1) /1000000.0F) * 1000);
printf("Finishing 3s-00");
return 0;
}
33. 33
Hilos y concurrencia
Threads and concurrency
hilo1_posix.cpp – hilo concurrente
#define MAX_THREADS 8
int *array;
int length = VECTOR_SIZE;
int count = 0;
int double_count = 0;
// main ………….
while(i < max_threads){
err = pthread_create(&tid[i], NULL, &count3s_thread, (void*)i);
if (err != 0)
printf("[3s-01] Can't create a thread [%ld]n",i);
else
printf("[3s-01] Thread created [%ld]n", i);
i++;
}
34. 34
Hilos y concurrencia
Threads and concurrency
hilo1_posix.cpp – hilo concurrente
#define MAX_THREADS 8
int *array;
int length = VECTOR_SIZE;
int count = 0;
int double_count = 0;
void *count3s_thread(void *arg){
int i;
int length_per_thread = length / max_threads;
long id = (long)arg;
int start = id * length_per_thread;
printf("nThread [%ld] starts [%d] length [%d]n", id, start,
length_per_thread);
for(i = start; i < start + length_per_thread; i++){
if (array[i] == 3){
count++;
}
}
}
35. 35
Hilos y concurrencia
Threads and concurrency
hilo1_posix.cpp – hilo concurrente
Los count y double_count no son
iguales
Desface en lo que cuentan los
hilos y la cuenta total
37. 37
Hilos y concurrencia
hilo2_posix.cpp – hilo concurrente
for(; i < max_threads; i++){
void *status;
int rc;
rc = pthread_join(tid[i],&status);
if(rc){
printf("ERROR; return code from pthread() is %d
n", rc);
exit(-1);
}else{
printf("Thread [%ld] exited with status [%ld] n", i,
(long)status);
}
}
La aplicación principal debería esperar a los hilos que se crearon
39. 39
Hilos y concurrencia
Problemas de Concurrencia
El count es la variable común
Tanto el hilo A y el hilo B
acceden a una variables común
¿Cómo podemos resolver ese
error?
40. 40
Hilos y concurrencia
Problemas de Concurrencia
Usar candados
Hilo A lee count y le pone un
candado para que ningún hilo
pueda escribir en esa variable
por otro hilo
Se vuelve un algoritmo
secuencial al acceso a datos
41. 41
Hilos y concurrencia
pthread_mutex_t mutex;
………………………..
pthread_mutex_lock(&mutex); // se llama esta sección
crítica
for(i = start; i < start + length_per_thread; i++){
if (array[i] == 3){
count++;
}
}
pthread_mutex_unlock(&mutex);
……………………
//El candado que evita la concurrencia
pthread_mutex_init(&mutex, NULL);
pthread_mutex_destroy(&mutex);
Existe una nueva variable t_mutex que
es un candado a traves pthread_mutex
En count3s_thread, cuando un hilo
comienza a contar pone un candado
sobre esa región de memoria donde se
va a modificar cont, esta parte del
código se le llama sección crítica
Inicialización el candado (mutex), la
variable mutex a sido inicializada de
forma global, todos los hilos la podrán
ver
43. 43
Hilos y concurrencia
El problema de la sección crítica
Sistema compuesto por n procesos
Cada uno tiene un fragmento de código: sección crítica
Los procesos deben acceder a la SC con exclusividad
Estructura general de cualquier mecanismo utilizado para resolver el problema de la
sección crítica:
Requisitos que debe ofrecer cualquier solución para resolver el problema
de la sección crítica:
Exclusión mutua
Progreso
Espera limitada
Entrada en la sección crítica
Código de la sección crítica
Salida de la sección crítica
44. 44
Hilos y concurrencia
//pthread_mutex_lock(&mutex);
for(i = start; i < start + length_per_thread;
i++){
if (array[i] == 3){
pthread_mutex_lock(&mutex);
count++;
pthread_mutex_unlock(&mutex);
}
}
//pthread_mutex_unlock(&mutex);
¿Qué sucede si se usa el bloqueo alrededor de declaraciones de código donde se
modifica la variable compartida?
47. 47
Hilos y concurrencia
// Definición de contadores privados para cada hilo
int private_count[MAX_THREADS];
**************************************
for(i = start; i < start + length_per_thread; i++){
if (array[i] == 3){
private_count[id]++; // cambia
}
}
// cambia... sección crítica
pthread_mutex_lock(&mutex);
count = count + private_count[id];
pthread_mutex_unlock(&mutex);
Se crea un arreglo de variables privadas
count
Cada hilo recorre el segmento que le
corresponde y podra contar con su
contador y bloquea el count fuera del for