2.2.1 Bibliotecas

Una biblioteca de hilos proporciona al programador una API (Application Programming Interface) para crear y administrar hilos.

Error al cargar imagen figura_7.png

Figura 7. Abstracción de biblioteca.


Hay dos formas principales de implementarla:

  • Primer enfoque: : Proporcionar una biblioteca ubicada en el espacio de usuario sin soporte de kernel.
    • Códigos y estructuras de datos para la biblioteca en espacio de usuario.
    • Invocar una función en la biblioteca da como resultado una llamada de función local en el espacio del usuario y no una llamada de sistema.
  • Segundo enfoque: Implementar una biblioteca a nivel de kernel soportada directamente por el SO.
    • Códigos y estructuras de datos para la biblioteca en espacio de kernel.
    • Invocar una función en la API para la biblioteca generalmente resulta en una llamada de sistema al kernel.

Existen dos estrategias generales para crear múltiples hilos:

  • Hilos asincrónicos: Una vez que el padre crea un hilo hijo, el padre reanuda su ejecución, de modo que el padre y el hijo se ejecutan de manera simultánea e independiente uno del otro.
    • Debido a que los hilos son independientes, generalmente hay poco intercambio de datos entre ellos.
  • Hilos sincrónicos: Se produce cuando el hilo principal crea uno o más hilos hijos y luego debe esperar a que todos sus hilos hijos finalicen antes de reanudarse.
    • Los hilos hijos realizan el trabajo en simultáneo, pero el padre no puede continuar hasta que este trabajo se haya completado.
    • Una vez finalizado cada hilo hijo se une (joins) con su padre. Solo después de que todos los hijos se hayan unido (joined), el padre puede reanudar la ejecución.
    • Por lo general, los hilos sincrónicos implican un intercambio de datos significativo entre ellos. Por ej.: el hilo padre puede combinar los resultados por sus hijos.

A continuación se presenta un ejemplo de código implementado en lenguaje de programación C.

                  #include <stdio.h>
                  #include <stdlib.h>
                  #include <pthread.h>
                  
                  // Estructura para pasar múltiples argumentos a la función del hilo
                    typedef struct {
                        int thread_id;
                        char* message;
                    } ThreadArgs;

                    // Función que se ejecutará en el primer hilo
                    void* threadFunction1(void* args) {
                        ThreadArgs* thread_args = (ThreadArgs*)args;
                        printf("Hilo %d: %s\n", thread_args->thread_id, thread_args->message);
                        pthread_exit(NULL); // Terminar el hilo
                    }

                    // Función que se ejecutará en el segundo hilo
                    void* threadFunction2(void* args) {
                        ThreadArgs* thread_args = (ThreadArgs*)args;
                        printf("Hilo %d: %s\n", thread_args->thread_id, thread_args->message);
                        pthread_exit(NULL); // Terminar el hilo
                    }

                    int main() {
                        // Definir la estructura de argumentos para cada hilo
                        ThreadArgs args1 = {1, "Hola desde el primer hilo!"};
                        ThreadArgs args2 = {2, "Hola desde el segundo hilo!"};

                        // Declarar las variables de hilo
                        pthread_t thread1, thread2;

                        // Crear los hilos y pasar las funciones junto con los argumentos
                        if (pthread_create(&thread1, NULL, threadFunction1, (void*)&args1) != 0) {
                            perror("Error al crear el primer hilo"); // Imprimir error si falla la creación del hilo
                            exit(EXIT_FAILURE);
                        }

                        if (pthread_create(&thread2, NULL, threadFunction2, (void*)&args2) != 0) {
                            perror("Error al crear el segundo hilo"); // Imprimir error si falla la creación del hilo
                            exit(EXIT_FAILURE);
                        }

                        // Esperar a que los hilos terminen su ejecución
                        if (pthread_join(thread1, NULL) != 0) {
                            perror("Error al unir el primer hilo"); // Imprimir error si falla la espera del hilo
                            exit(EXIT_FAILURE);
                        }

                        if (pthread_join(thread2, NULL) != 0) {
                            perror("Error al unir el segundo hilo"); // Imprimir error si falla la espera del hilo
                            exit(EXIT_FAILURE);
                        }

                        // Imprimir mensaje del hilo principal
                        printf("Hilo principal: Ambos hilos han terminado su ejecución.\n");

                        return 0;
                    }
              
Código 2. Hilos asíncronos.

La ejecución asíncrona significa que los hilos pueden ejecutarse en paralelo, y su progreso no sigue un orden predecible. La función pthread_join se utiliza para sincronizar el hilo principal con los hilos secundarios, permitiendo al programa principal esperar a que los hilos completen sus tareas antes de continuar. Esto es útil para coordinar y gestionar la ejecución de hilos en un programa.

Reflexiona la siguiente pregunta que será tratada en la sesión de clase.

¿Qué funciones desconoces del código 2 mostrado anteriormente?