Suma eficiente de números utilizando hilos
Tabla de Contenidos
- Introducción
- Conceptos básicos de argumentos en hilos (threads)
- Paso de argumentos a hilos
- División del arreglo de números
- Implementación del programa
- Cambiando el número de hilos
- Uso de un cerrojo mutex (emma lock)
- Sumando los elementos del arreglo
- Retornando valores desde los hilos
- Dos opciones para manejar la memoria dinámica
- Uso de punteros
- Liberación de memoria
- Sumando los resultados en el hilo principal
- Conclusiones
Introducción
En este ejemplo práctico, vamos a explorar cómo pasar argumentos a hilos (threads) y sumar los elementos de un arreglo de números de manera eficiente utilizando la programación multi-hilo. El objetivo final es obtener la suma total de todos los números en el arreglo "primes". Veremos cómo dividir el arreglo en partes iguales y asignar cada parte a un hilo diferente para que realice la suma parcial. Luego, sumaremos todas las sumas parciales en el hilo principal y obtendremos el resultado final. Este enfoque nos permite aprovechar al máximo el poder de los procesadores multi-núcleo y acelerar el cálculo de la suma.
Conceptos básicos de argumentos en hilos (threads)
Antes de sumergirnos en la implementación del programa, repasaremos algunos conceptos básicos sobre la forma en que los hilos (threads) manejan los argumentos. En nuestro caso, queremos pasar el índice de inicio del sub-arreglo que cada hilo debe sumar. Para lograr esto, haremos uso de la función "pthread_create" que acepta un puntero a una función como argumento. En esta función, podemos pasar cualquier tipo de datos utilizando un solo parámetro genérico. En nuestro caso, usaremos un puntero a un objeto en la memoria.
Paso de argumentos a hilos
Para pasar el argumento requerido a cada hilo, necesitaremos modificar ligeramente nuestra función genérica de inicio del hilo. Necesitamos garantizar que la primera mitad del arreglo se pase al primer hilo y la segunda mitad se pase al segundo hilo. Para lograr esto, utilizaremos un sencillo cálculo matemático. Cuando el índice "i" es 0, el primer hilo sumará los elementos del índice 0 al 4. Cuando el índice "i" es 1, el segundo hilo sumará los elementos del índice 5 al 9. Así sucesivamente.
Implementación del programa
Ahora que hemos entendido los conceptos básicos, podemos pasar a la implementación del programa. Primero, necesitamos cambiar el número de hilos a 2 para este ejemplo. Luego, debemos utilizar un cerrojo mutex (emma lock) para asegurarnos de que los hilos accedan a la memoria compartida de manera segura.
Cambiando el número de hilos
Para cambiar el número de hilos, es necesario modificar un par de líneas de código. En lugar de tener 10 hilos como en el ejemplo original, ahora usaremos solo 2. Esto se logra cambiando el valor del bucle "for" que crea los hilos. En este caso, estableceremos el valor de la variable "i" de 0 a 1. Además, debemos asegurarnos de que el argumento que se pasa a cada hilo sea correcto. En lugar de simplemente usar "i" como el índice de inicio, utilizaremos la fórmula "i * 5" para obtener el índice adecuado.
Uso de un cerrojo mutex (emma lock)
Para garantizar que los hilos accedan a la memoria compartida de manera segura, utilizaremos un cerrojo mutex (emma lock) en nuestro programa. Esto ayudará a prevenir problemas como las condiciones de carrera. En nuestro caso, necesitamos proteger la variable "global sum" que almacenará la suma total de todos los hilos. Cada hilo sumará los elementos de su sub-arreglo y luego agregará su suma parcial a la variable "global sum". Una vez que hayamos sumado todos los valores, obtendremos el resultado final.
Sumando los elementos del arreglo
Dentro del bucle "for" que recorre el sub-arreglo asignado a cada hilo, realizaremos la suma de los elementos. Utilizaremos la variable "sum" para almacenar la suma parcial en cada hilo. Para hacer esto, utilizaremos un bucle "for" que suma los elementos del sub-arreglo al valor de "sum". La fórmula utilizada es "sum += primes[index + i]". Aquí, "index" es el índice inicial del sub-arreglo y "i" varía de 0 a 4.
Retornando valores desde los hilos
Ahora llegamos a un punto importante: cómo retornar los valores desde los hilos. En nuestro caso, necesitamos obtener la suma parcial de cada hilo y sumarla a la variable "global sum" en el hilo principal. Hay dos opciones para manejar esto: podemos asignar otra área de memoria dinámica y retornarla desde el hilo, o simplemente retornar la memoria dinámica que ya hemos asignado. En este programa, optaremos por la segunda opción.
Dos opciones para manejar la memoria dinámica
Cuando asignamos memoria dinámica para almacenar la suma parcial de cada hilo, tenemos dos opciones para manejarla correctamente. La primera opción es asignar una nueva área de memoria utilizando la función "malloc" y retornarla desde el hilo. Luego, en el hilo principal, deberíamos liberar esta memoria utilizando la función "free". La segunda opción, que utilizaremos en este programa, es simplemente retornar la memoria dinámica que ya hemos asignado. Esto evita la necesidad de asignar más memoria y permite que el hilo principal sea responsable de liberarla adecuadamente.
Uso de punteros
Para retornar la memoria dinámica desde el hilo, necesitamos utilizar punteros. En nuestro caso, utilizaremos un puntero doble (double pointer) ya que queremos que el puntero retornado apunte al mismo lugar en la memoria que el puntero original. Esto nos permitirá acceder a la suma parcial desde el hilo principal.
Liberación de memoria
Es importante liberar la memoria que hemos asignado dinámicamente para evitar fugas de memoria. Una buena práctica es tener el mismo número de llamadas a "free" que tenemos llamadas a "malloc" o "calloc". En nuestro caso, debemos asegurarnos de liberar la memoria en el hilo principal después de haber calculado la suma total. Utilizaremos la función "free" para liberar la memoria que hemos pasado entre los hilos.
Sumando los resultados en el hilo principal
Finalmente, llegamos al último paso: sumar todos los resultados en el hilo principal. Utilizaremos una variable llamada "global sum" para almacenar la suma total de todos los hilos. Inicializaremos esta variable en 0 antes de comenzar los hilos. Luego, dentro del bucle "for" que crea los hilos, sumaremos la referencia de la variable "r" a la "global sum". Finalmente, imprimiremos el valor de "global sum", que debería ser igual a la suma total de todos los elementos en el arreglo "primes".
Conclusiones
En este artículo, hemos explorado cómo pasar argumentos a hilos y sumar los elementos de un arreglo de números utilizando programación multi-hilo en C. Hemos desglosado los conceptos básicos y luego hemos implementado un programa práctico paso a paso. Esta técnica puede ser útil cuando trabajamos con grandes conjuntos de datos que se pueden dividir en subconjuntos más pequeños y son adecuados para ser procesados en paralelo. Al aprovechar las capacidades de procesamiento de los procesadores multi-núcleo, podemos acelerar el tiempo de ejecución y mejorar la eficiencia de nuestros programas.
Highlights:
- La programación multi-hilo permite acelerar el procesamiento de grandes conjuntos de datos.
- Es posible pasar argumentos a hilos utilizando punteros.
- El uso de cerrojos mutex (emma lock) ayuda a garantizar el acceso seguro a la memoria compartida.
- Es importante liberar la memoria dinámica asignada adecuadamente para evitar fugas de memoria.
- Sumar los resultados en el hilo principal permite obtener el resultado final.
Preguntas frecuentes:
P: ¿La cantidad de hilos puede ser mayor a 2?
R: Sí, se puede modificar el programa para utilizar cualquier cantidad de hilos.
P: ¿Por qué se utiliza un cerrojo mutex (emma lock)?
R: El cerrojo mutex ayuda a prevenir problemas como las condiciones de carrera cuando varios hilos intentan acceder a la memoria compartida al mismo tiempo.
P: ¿Es necesario liberar la memoria asignada dinámicamente?
R: Sí, es importante liberar la memoria asignada dinámicamente para evitar fugas de memoria y garantizar una gestión de memoria adecuada.
Recursos: