Tema 2.2. Hilos y Concurrencia PDF
Document Details
Uploaded by BelievablePlatinum
Universidad Camilo José Cela (UCJC)
Tags
Summary
Este documento presenta una introducción a los conceptos de procesos, hilos y concurrencia. Se describe la diferencia entre programación secuencial y en paralelo, enfocándose en los procesos ligeros (threads). Se exploran temas como la gestión de procesos por el sistema operativo, la estructura y estados de los procesos ligeros. Además, se menciona la memoria compartida y el paso de mensajes como mecanismos de comunicación entre procesos.
Full Transcript
TEMA 2.2. HILOS Y CONCURRENCIA Índice de contenidos 1. Proceso 2. Threads y paralelismo. 3. Escenarios de aplicaciones multithread. 4. Servidores. 5. Ejemplo: Ejecución en serie VS. Paralela. 6. Ejercicio (Entra) 7. Algunas...
TEMA 2.2. HILOS Y CONCURRENCIA Índice de contenidos 1. Proceso 2. Threads y paralelismo. 3. Escenarios de aplicaciones multithread. 4. Servidores. 5. Ejemplo: Ejecución en serie VS. Paralela. 6. Ejercicio (Entra) 7. Algunas funciones del módulo threading. 8. Algunas funciones del módulo multiprocessing. 9. Threads de tipo Daemon (saber diferencia de daemon , procesos ligeros, procesos secuenciales) 10. Condiciones de carrera. 11. Ejercicios. 12. Programación concurrente. 13. Comunicación entre procesos. 14. Monoprocesador vs. Multiprocesador. 15. Conceptos previos. 16. Modelos típicos de comunicación y sincronización (Saber qué son y cómo funcionan) 1. Proceso 1.1 De nició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 (RAM). 1.2 Árbol de procesos Relación de jerarquía. Hay un proceso padre y de este nacen los diversos procesos padres, en UNIX el proceso padre sería init. 1.3 Información de los procesos El sistema operativo mantiene un conjunto de estructuras de información que permiten identi car y gestionar los procesos. Para ello, usa la tabla de los procesos, dentro de la tabla del SO. 1.4 Proceso ligero o thread (Entra) Un proceso ligero, hilo o thread es un programa en ejecución ( ujo de ejecución) que comparte la imagen de memoria (y otras informaciones) con otros procesos ligeros (dentro de un proceso): - Unidad de ejecución dentro de un proceso. - Dependiendo del número de procesos que puedan ejecutar simultáneamente, un SO es monotarea o multitarea. Al estar dentro de un proceso comparten información con otros hilos. De forma que un hilo esta dentro de un proceso, por lo los recursos que tienen los procesos también los tienen los hilos. Un proceso puede contener un solo ujo de ejecución (monothread) o varios (multithread): - Por ejemplo, en el lenguaje C el hilo de ejecución primario se corresponde con la función principal main. Página 1 fi fl fl fi 1.5 Información propia/compartida Los hilos comparten la información de los procesos, ya que están dentro de un proceso. Por thread (información propia): - Contador de programa, valores de los registros. - Pila. - Estado (ejecutando, listo o bloqueado). Por proceso (información compartida): - Espacio de memoria. - Variables globales. - Ficheros abiertos, descriptores de sockets. - Procesos hijos. - Temporizadores. - Señales y semáforos. - Contabilidad. 1.6 Estructura de un proceso con threads 1.7 Mapa de memoria de un proceso con threads Cuando un proceso se manda, se ejecuta un mapa de memoria, el cual conoce todo lo que necesita un proceso para ejecutarse. En dicho mapa se van a declarar las variables y los datos sin valor inicial (se declaran como variables pero no tienen ningún valor asignado). Fichero —> Se proyecta de la ejecución del un programa a un código. Pila —> Manda información a los hilos. 1.8 Estados del proceso ligero 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. - Bloqueado por comunicación: Bloqueante síncrona. - Bloqueada por acceso a disco: Se esta haciendo otra cosa en el disco. Posible pregunta: Lanza un proceso o un proceso ligero, donde la asignación de cada uno de ellos va a ser distinta. Página 2 2. Threads y paralelismo Se busca una ejecución en paralelo, se ejecutan varios procesos a la vez. 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. 2.1 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”. Se busca hacer proceso ligeros, varias tareas que se ejecutan en paralelo. No hay un bloqueo a nivel de aplicación, pero si un proceso que se bloquea al trabajar en paralelo. Cada hilo incluye funciones, cada cada una asignada a un hilo independiente, pero comparten recursos. 3. Escenarios de aplicaciones multithread Cuando se desea maximizar el uso de los recursos: Plataformas multicore. Cuando se desea solapar la ejecución con actividades de E/S bloqueantes: - En una aplicación single-thread el proceso se bloquea cuando se realiza una operación de E/S bloqueante (lectura de un chero, espera de un mensaje…). Cuando tenemos tareas de entrada / salida bloqueantes, se buscan que se puedan ejecutar varias a la vez (multithread). 4. Servidores 4.1 Servidores secuenciales Hay un cliente que realiza una solicitud a un servidor. Se bloquea el cliente hasta que el server envíe la solicitud. No es un proceso en paralelo. Sin embargo, cada solicitud crea un hilo en paralelo, estos acceden al recurso, y también dan la respuesta al cliente. Entonces, la cronología es: Cliente —> Servidor —> Thread —> Cliente. De forma que en la mayoría de los casos el cliente no accede al recurso. Así, los procesos concurrentes deben comunicar y sincronizarse. 4.2 Servidores concurrentes con threads Los procesos concurrentes deben comunicar y sincronizarse. 4.3 Diseño de servidores mediante threads Distintas arquitecturas de SW para construir servidores paralelos: - Un proceso distribuidor que acepta peticiones y las distribuye entre un pool de procesos ligeros. - Trabajador: Cada proceso ligero realiza las mismas tareas: aceptar peticiones, procesarlas y devolver su resultado. - Segmentación: Cada trabajo se divide en una serie de fases, cada una de ellas se procesa por un proceso ligero especializado. Página 3 fi 5. Ejemplo: Ejecución en serie VS. Paralela Paralelismo —> Ahorra tiempo de ejecución cuando hay que ejecutar tareas distintas. 6. Ejercicio (ENTRA) Se desea comparar el rendimiento de un servidor de cheros secuencial con uno multi ujo que utiliza procesos ligeros (threads). En ambos casos el servicio de una petición implica la ejecución con 10 ms de tiempo de cómputo y en el caso de que los datos pedidos no estén en la cache de bloques del servidor, otros 40 ms de acceso al disco. En el servidor secuencial, durante el acceso al disco se bloquea el proceso servidor. Sin embargo, en el segundo caso sólo se bloquea el thread correspondiente. Teniendo en cuenta que el disco sólo puede llevar a cabo una operación en cada momento y suponiendo que el servidor se ejecuta en un sistema con un único core se pide: a) Suponiendo que no se producen aciertos en la cache, ¿Cuántas peticiones por segundo puede procesar el servidor secuencial? ¿y un servidor multi ujo? b) Igual que el anterior apartado, pero con un 100% de aciertos en la cache de bloques. c) Igual para un 50% de aciertos. d) Considerar cómo afectaría a los resultados de los tres apartados anteriores el uso de un procesador con 4 núcleos para ejecutar el servidor. Solución a) Secuencial. Análisis teniendo en cuenta máxima capacidad del servidor y sin limitación de recursos: - Peticiones por segundo = 1 s / (Tiempo total por petición) - Peticiones por segundo = 1000 ms / 50 ms = 20 peticiones/s Multithread. El tiempo de cómputo de 10 ms puede ejecutarse en paralelo con el bloqueo del thread durante el acceso al disco. - Peticiones por segundo = 1000 ms / 40 ms = 25 peticiones/s Dado que se tienen aciertos del 100% en la caché, no se necesita acceder al disco para satisfacer las solicitudes. b) Secuencial: - Peticiones por segundo = 1000 ms / 10 ms = 100 peticiones/s Multithread: - Peticiones por segundo = 1000 ms / 10 ms = 100 peticiones/s c) Secuencial. El tiempo total por petición es la suma de los tiempos de cómputo (10 ms) y el tiempo de acceso al disco (40 ms para el 50% de las solicitudes). Tiempo medio = 50*0,5 + 10*0,5 = 30 ms - Peticiones por segundo = 1000 ms / 30 ms = 33 peticiones/s Multithread. Llegan 4 peticiones cada 40 ms. Cada 40 ms se completa una petición de disco y dos peticiones por acierto, dando lugar a 3 peticiones por cada 40 ms. Página 4 fl fi fl - Peticiones por segundo = 1000 ms * 3 / 40 ms = 75 peticiones/s d) 100 % de aciertos en caché: - Peticiones por segundo = 4 x 1000 ms / 10 ms = 400 peticiones/s 50 % de aciertos en caché: Llegan 16 (4 veces más) peticiones cada 40 ms. Cada 40 ms se completa 1 petición de disco y 8 peticiones por acierto, dando lugar a 9 peticiones por cada 40 ms. - Peticiones por segundo = 1000 ms x 9 / 40 ms = 225 peticiones/s 7. Algunas funciones del módulo threading threading.active_counts: Devuelve el número de threads actualmente ejecutando. threading.current_thread(): Devuelve el objeto thread actual que se corresponde con el que realiza la llamada. threading.get_ident(): Devuelve el identi cador del thread que realiza la llamada. threading.enumerate(): Devuelve la lista de todos los threads actualmente ejecutando. 8. Algunas funciones del módulo multiprocessing len(multiprocessing.active_children()): Obtener el número de procesos activos. multiprocessing.current_process(): Obtener el proceso actual. current_process.ident: Obtener el identi cador del proceso actual. multiprocessing.active_children(): Enumerar todos los procesos activos. 9. Threads de tipo Daemon Subprocesos que siempre se ejecutan en segundo plano. No bloquea la nalización del programa principal. - No impide que el programa principal termine su ejecución de manera normal. Brindan soporte a subprocesos principales o que no son daemon. Ideal para tareas de monitorización, actualizaciones en segundo plano, y otras operaciones que no son críticas para la ejecución principal del programa. En Python los threads independiente se denominan Daemon, no es necesario esperar la terminación de estos threads. ¿Pueden producirse estas salidas? La respuesta es SI. 10. Condiciones de carrera Un programa tiene una condición de carrera si varios procesos (threads) acceden a un recurso compartido sin control de forma que el resultado depende del orden de ejecución. - En el ejemplo anterior el recurso compartido es la dirección de memoria donde se almacena la variable i (de la función main). - A cada thread se le pasa la misma dirección de memoria y el contenido de la misma va cambiando en la ejecución del bucle for. Página 5 fi fi fi 11. Ejercicios 11.1 Ejercicio 1 Se desea desarrollar una aplicación que debe realizar dos tareas que se pueden ejecutar de forma independiente. Los códigos de estas dos tareas se encuentran de nidos en dos funciones cuyos prototipos en lenguaje de programación Python, son los siguientes: Programar la aplicación anterior utilizando tres modelos distintos: - Un programa secuencial ejecutado por un único proceso. - Un programa que crea procesos para desarrollar cada una de las tareas. - Un programa que realiza las tareas anteriores utilizando procesos ligeros. En cualquiera de los tres casos la aplicación debe terminar cuando todas las tareas hayan acabado. 11.2 Ejercicio 2: Programa secuencial 11.3 Ejercicio 3: Programa utilizando procesos 11.4 Ejercicio 4: Programa utilizando procesos ligeros 12. Programación concurrente 12.1 SSOO multitarea En SSOO multitarea pueden coexistir varios procesos activos a la vez: - Multiprogramación con un único procesador. - Multiprocesador. - Multicomputador. Página 6 fi En general, para p procesadores y n procesos, la concurrencia es: - Aparente si n > p. - Real si n