Download Fundamentos Básicos de Threads - Facultad de Ciencias Exactas

Document related concepts

Proceso inactivo del sistema wikipedia , lookup

Seguridad en hilos wikipedia , lookup

Monitor (concurrencia) wikipedia , lookup

Espera activa wikipedia , lookup

RTAI wikipedia , lookup

Transcript
Lenguajes de Programación I
Año 2006
UNIVERSIDAD NACIONAL DEL CENTRO DE LA PROVINCIA DE BUENOS AIRES
FACULTAD DE CIENCIAS EXACTAS
FUNDAMENTOS BÁSICOS DE THREADS
por José Macchi & Martín Mezzanotte
ANTES DE COMENZAR…
En este tutorial nos proponemos brindar una ayuda al alumno para familiarizarse con el
concepto de thread, su significado, aspectos teóricos y características.
Además examinaremos aspectos de multiprocesamiento (y multithreading),
estudiaremos someramente algunos conceptos básicos de teoría de threads y
observaremos cómo los threads pueden ser efectivamente usados para crear programas
más eficientes. A tal efecto, nos centraremos en los aspectos de funcionamiento de los
programas de múltiples threads, así como en las distintas instancias de procesamiento y
administración de los mismos.
¿QUÉ ES UN THREAD?
Un thread o hilo es un “semi-proceso”, que tiene su propia pila, y que ejecuta una
porción de código dada. A diferencia de un proceso real, un thread normalmente
comparte su memoria con otros threads (en la cual, tal como sucede con los procesos,
cada thread tendrá asignado su espacio de memoria).
Un grupo de threads es un conjunto de “hilos de ejecución” que están corriendo todos
dentro del mismo proceso. Dado que comparten todos la misma porción de memoria,
pueden acceder a las mismas variables globales, la misma memoria de heap, los mismos
descriptores de archivos, etc. Todos corren en paralelo (por ejemplo: usando porciones
del tiempo asignado al proceso en general o, si están dentro de un sistema con
multiprocesadores, pueden eventualmente correr de forma paralela realmente).
La ventaja de usar un grupo de threads en lugar de un programa normal en serie es que
muchas operaciones pueden ser llevadas a cabo de forma paralela y, de esta forma, los
eventos asociados a cada actividad pueden ser manejados inmediatamente tan pronto
como llegan (por ejemplo: si tenemos un thread manejando la interface de usuario y otro
manejando las consultas a una base de datos, podremos ejecutar consultas complejas
realizadas por el usuario y aun así responder a la entrada del mismo mientras la consulta
está siendo ejecutada).
La ventaja de usar un grupo de threads en vez de un grupo de procesos es que el cambio
de contexto entre threads es realizado mucho más rápidamente que el cambio de
contexto entre procesos (un cambio de contexto significa que el sistema operativo
Lenguajes de Programación I
Año 2006
cambia la ejecución de un thread o proceso a la ejecución de otro). Por lo tanto, las
comunicaciones entre dos threads son usualmente más rápidas y sencillas de
implementar que las comunicaciones entre dos procesos.
Por otro lado, debido a que los threads dentro de un grupo comparten el mismo espacio
de memoria, si uno de ellos corrompe el espacio de su memoria, los otros threads
también sufrirán las consecuencias. Con un proceso, el sistema operativo normalmente
protege a un proceso de otros y si un proceso corrompe su espacio de memoria los
demás no se verán afectados.
BENEFICIOS DE THREADS VS PROCESOS
Si se los implementa correctamente entonces los threads tienen algunas ventajas por
sobre los (multi) procesos, es así que tendremos:




Menos cantidad de tiempo para crear un nuevo thread respecto de un proceso,
porque el nuevo thread usa la dirección de espacio del proceso actual.
Menos tiempo en la terminación de un thread respecto de un proceso.
Menos tiempo en el cambio de ejecución entre dos threads dentro del mismo
proceso, debido a que ambos están usando el mismo espacio de direcciones del
proceso.
Menor cantidad de overhead en las comunicaciones; dado que la comunicación
entre los threads de un proceso es simple y al compartir recursos comunes,
particularmente el espacio de direcciones, la información creada por un thread
estará disponible para todos los demás.
MULTITHREADING VS. SINGLE THREADING
Así como podemos tener múltiples procesos corriendo en nuestra PC, también podemos
tener múltiples threads corriendo. De esta forma aparecen dos grandes grupos de
sistemas operativos respecto de la forma de ejecución de threads.
Single threading
-- cuando el sistema operativo no reconoce al concepto de thread.
Multithreading
-- cuando el sistema operativo soporta múltiples threads de ejecución dentro de
un proceso.
La figura 1 se muestra la variedad de modelos de ejecución para threads y procesos.
Lenguajes de Programación I
Año 2006
Figura 1 – Modelos de Ejecución
Algunos ejemplos de sistemas operativos populares para cada uno de los modelos
presentados en la figura anterior son:
MS-DOS
-- soporta solo un único proceso con un único thread (cuadrante superior
izquierdo de la figura).
UNIX
-- soporta múltiples procesos pero un único thread por proceso (cuadrante
inferior izquierdo de la figura).
Solaris
-- soporta múltiples threads (en uno o más procesos).
El Multithreading tiene ciertas ventajas respecto de Single Threading:




Mejora la velocidad de respuesta de las aplicaciones.
Utiliza los múltiples procesadores de una computadora (si los hubiere) más
eficientemente.
Mejora la estructura del programa; dado que muchos programas son más
eficientemente estructurados si se los desarrolla como unidades de ejecución
independientes o semi-independientes, en lugar de ser programas monolíticos.
Usan menos recursos del sistema, fundamentalmente debido a que los espacios
de direcciones se comparten y a que -por ello- la comunicación entre unidades
de ejecución se simplifica.
La Figura 2 ilustra los diferentes modelos de procesos y controles de threads en una
aplicación single thread y multithreaded.
Lenguajes de Programación I
Año 2006
Figura 2 – Aplicaciones de Unico y Multiples Threads
Como podemos observar en la Figura 2, los espacios de direcciones y el bloque de
control del proceso son compartidos por los esquemas de múltiples threads; mientras
que en el caso de los threads únicos esto no sucede, teniendo espacios y bloques de
control únicos por cada proceso.
NIVELES DE IMPLEMENTACION DE THREADS
Desde el punto de vista de la teoría de Lenguajes de Programación, básicamente existen
dos grandes categorías en lo que respecta a las implementaciones, manejos y
administración de threads:


Threads de Nivel del Lenguaje – Bibliotecas que administran Threads.
Threads de Nivel del Sistema Operativo – Llamadas al sistema implementadas
por los sistemas operativos.
Ambos tipos de implementaciones tienen sus ventajas, y en realidad existen algunos
sistemas operativos que permiten el acceso a ambos niveles (ejemplo: Solaris).
Threads de Nivel del Lenguaje (TNL)
En este nivel, el kernel del sistema operativo no se preocupa por la existencia de los
threads. Toda la administración de los threads es realizada a nivel del lenguaje, es decir:
por la aplicación, usando alguna biblioteca de threads. Los cambios entre Threads
durantes la ejecución no requieren de la intervención del kernel, ni de sus privilegios y
la planificación de los mismos es específica de la aplicación.
Lenguajes de Programación I
Año 2006
En algunos textos los TNL suelen denominarse también threads de nivel de usuario.
Ventajas e inconvenientes de TNL
Ventajas:



El cambio entre threads no involucra al sistema operativo.
La planificación puede ser especifica de la aplicación mejorando así la
performance al seleccionar el algoritmo que más conveniente.
TNLs pueden correr en cualquier sistema operativo, sólo necesitan de las
bibliotecas de administración de threads.
Desventajas:


La mayoría de las llamadas al sistema son bloqueantes y como el kernel bloquea
procesos (no conoce de threads) entonces terminará bloqueando a todos los
threads dentro de un mismo proceso.
El kernel puede sólo asignar procesadores a los procesos, por lo que dos threads
dentro de un mismo proceso no podrán correr en forma simultánea, ya que
disponen de un único procesador para correr.
Threads de Nivel de Sistema Operativo (TNSO)
A diferencia del nivel anterior, en este nivel la administración de los threads es realizada
por el kernel del sistema operativo, por ende no existen bibliotecas de threads pero
puede existir un API (de llamadas al sistema) que el kernel facilita para su utilización.
El kernel mantiene la información del contexto de los procesos y los threads, es así que
los cambios entre threads requieren la intervención del kernel en el proceso de
planificación de la ejecución de los mismos.
En algunos textos los TNSO suelen denominarse también threads de nivel de kernel.
Ventajas e Inconvenientes de los TNSO
Ventajas


El kernel puede simultáneamente administrar varios threads de un mismo
proceso en varios procesadores.
Las rutinas del kernel pueden ser multithreaded.
Desventajas:

Los cambios entre threads dentro de un mismo proceso involucran la
intervención del kernel, pudiendo generar demoras en el tiempo de ejecución.
Aproximaciones combinadas de TNL/TNSO
También existen modelos híbridos, donde la idea central es combinar lo mejor de cada
una de las aproximaciones anteriores. Solaris es un ejemplo de un sistema operativo que
combina TNL y TNSO.
Lenguajes de Programación I
Año 2006
ADMINISTRACIÓN DE LA MEMORIA CON THREADS
Podemos decir que la planificación de la ejecución de los threads es una clara diferencia
entre ambos modelos de implementación de threads: en el TNL la planificación es
llevada a cabo a nivel del lenguaje, mientras que en el TNSO es llevada a cabo a nivel
de sistema operativo. Sin embargo, aún no hemos estudiado a qué nivel se efectúa la
administración de la memoria. Es decir: la manera en que cada thread accede a los datos
compartidos por los restantes threads o a sus propios datos.
Es importante señalar que en una aplicación de múltiples threads no todos los datos
pueden ser compartidos entre los threads; sino que cada thread posee una pila propia de
ejecución, donde cada uno de sus procedimientos o funciones alojan (en sus respectivos
registros de activación) las variables semiestáticas, semidinámicas, descriptores, etc.
Los datos almacenados en cada pila de ejecución sólo son accesibles por el thread
propietario de dicha pila. En cambio, los datos almacenados en el heap y los datos
almacenados en forma estática pueden ser compartidos por todos los threads del
proceso; razón por la cual, puede ser necesario establecer mecanismos de sincronización
que controlen el acceso a estos datos. En este esquema, la forma en que se administre el
espacio de memoria del proceso es crucial para garantizar una ejecución correcta de la
aplicación.
Respecto de la administración de la memoria, puede decirse que el sistema operativo
provee a la aplicación de una porción de memoria, a través de una cierta política que
depende del sistema que utilicemos. Luego, esta porción de memoria debe se
administrada (por el lenguaje o por el S.O.) en función de las características de la
aplicación y del lenguaje de programación en cuestión. En este punto deben tomarse
decisiones tales como ¿qué cantidad de memoria se asigna al proceso? ¿qué cantidad de
memoria del proceso se asigna a cada thread? ¿cómo y dónde se ubican las pilas de
ejecución de cada thread?; etc.
Para contestar adecuadamente estos interrogantes es necesario conocer cierta
información sobre la forma en que se ejecutarán los diferentes threads de la aplicación.
Por ejemplo: si un thread necesita mucho más espacio de memoria que otros threads
necesitaría ser alojado en un sector de memoria lo suficientemente amplio, como para
atender sus demandas de memoria sin afectar a otras porciones de datos. Dado que no
siempre es posible inferir este tipo de conocimiento, las estrategias de administración de
memoria pueden diferir. Por ende, puede ocurrir que la administración de memoria sea
llevada a cabo por el S.O. o por el Lenguaje.
Frecuentemente, cuando no es posible conocer las características de ejecución de los
threads, es el S.O. el que efectúa las tareas de administración de la memoria. Si, en
cambio, el usuario del lenguaje (el programador) conoce esta información puede
determinar la política de administración de memoria mediante instrucciones al
compilador de la aplicación; o sea, establecer una administración a nivel del lenguaje.
Lenguajes de Programación I
Año 2006
IMPLEMENTACIONES
Existen muchas y muy diferentes e incompatibles versiones de threading. Éstas incluyen
implementaciones tanto a nivel de usuario como a nivel de kernel.
Debemos notar que los trheads pueden ser implementados sin soporte del sistema
operativo, aunque algunos sistemas operativos o bibliotecas proveen soporte explícito
para ellos. Por ejemplo, las versiones recientes de Microsoft Windows (Windows NT
3.51 SP3 y posteriores) soportan el API de threads para aplicaciones que quieren
mejorar su performance mediante el uso de sus propios esquemas de administración, en
lugar de dejar dicha actividad al planificador del sistema operativo. Microsoft SQL
Server 2000 en el modo de planificación de usuario es un ejemplo de ello.
Ejemplos de implementaciones a nivel de Sistema Operativo




Light Weight Kernel Threads en varios BSDs.
M:N threading (en BSDs).
Native POSIX Thread Library para Linux, una implementación de POSIX
Threads (pthreads) standard.
Apple Multiprocessing Services (en versiones 2.0 y posteriores).
Ejemplos de Implementaciones de nivel de Lenguaje




GNU Portable Threads [1]
FSU Pthreads
Apple Computer's Thread Manager
Threads cooperativos de REALbasic
Ejemplos de Implementaciones Híbridas

Scheduler activations usadas por la biblioteca native POSIX de NetBSD
BIBLIOGRAFÍA
 http://users.actcom.co.il/~choo/lupg/tutorials/debugging/debugging-with-gdb.html
 http://users.actcom.co.il/~choo/lupg/tutorials/multi-thread/multi-thread.html
 http://www.cs.cf.ac.uk/Dave/C/node29.html
 http://en.wikipedia.org/wiki/Thread_(computer_science)