HilosPython1.5#
Hilos (Threads) en Python#
Permiten aprovechar las capacidades multiprocesador para ejecutar varias instrucciones a la vez, como subprocesos independientes.
Chapter 2: Thread-based Parallelism
https://github.com/jsdnhk/python-parallel-programming-cookbook-code/tree/master/Chapter 2
¿Cómo utilizar los hilos?#
La forma más sencilla de usar un subproceso es instanciarlo con una función de destino y luego llamar al método start() para que comience a funcionar.
El subprocesamiento del módulo de Python tiene el método Thread():
class threading.Thread(group=None, target=None, name=None, args=(), kwargs={})
group: Este es el valor del grupo que debe ser Ninguno; esto está reservado para futuras implementaciones
target: esta es la función que se ejecutará cuando inicie una actividad de subproceso
name: Este es el nombre del hilo; por defecto, se le asigna un nombre único de la forma Thread-N
args: esta es la tupla de argumentos que se pasarán a un destino
kwargs: este es el diccionario de argumentos de palabras clave que se utilizarán para la función de destino
import threading # Manejo de hilos
from time import sleep # Pausar tiempo
import random # Números aleatorios
def function(i):
sleep(1.5*random.random()) #Tiempo de 0<t<1.5
return print ("Función llamada por el hilo %i" %i)
for i in range(5):
t = threading.Thread(target=function, args=(i,)) # Instanciarlo con una función de destino
t.start() # Comience a funcionar
#t.join() # Esperar por otro hilo
Función llamada por el hilo 0
Función llamada por el hilo 1
Función llamada por el hilo 2
Función llamada por el hilo 3
Función llamada por el hilo 4
¿Cómo determinar el hilo actual?#
Cada instancia de Thread asigna un nombre de forma predetermianda.
El uso de argumentos para identificar o nombrar el subproceso es innecesario.
Nombrar subprocesos es útil en procesos de servidor con múltiples subprocesos de servicio que manejan diferentes operaciones
import threading # Manejo de hilos
import time # librería para tiempo
def first_function():
print (threading.currentThread().getName()+str(' iniciando...\n'))
time.sleep(1)
print (threading.currentThread().getName()+str(' finalizó\n'))
return
def second_function():
print (threading.currentThread().getName()+str(' iniciando...\n'))
time.sleep(5)
print (threading.currentThread().getName()+str(' finalizó \n'))
return
t1 = threading.Thread(name='Thread-Nombre', target=first_function) # Nombre de hilo asignado
t2 = threading.Thread( target=second_function) # Toma nombre de hilo por defecto automáticamente
t1.start()
t2.start()
Thread-Nombre iniciando...
Thread-21 iniciando...
Thread-Nombre finalizó
Thread-21 finalizó
¿Cómo crear un hilo con temporizador de inicio?#
# Crear y ejecutar hilos con temporizador
import threading
def ejecutar(tiempo_s):
print(f'El hilo {threading.current_thread().name} te saluda luego de tu espera de {tiempo_s} segundos')
# creamos un temporizador
tiempo_s = 5
temporizador = threading.Timer(tiempo_s, function=ejecutar, args=(tiempo_s,)) # Crear el hilo con temporizador
temporizador.start() # El hilo empezará cuando pasen segundos dados
print("No te vayas, espera...")
No te vayas, espera...
El hilo Thread-23 te saluda luego de tu espera de 5 segundos
¿Cómo crear hilos, ejecutarlos y que el principal espere?#
import threading # Manejo de hilos
from time import sleep # Pausar tiempos
import random # Números aleatorios
def ejecutar():
print(f'Comienza {threading.current_thread().name}')
sleep(1.5*random.random()) #Tiempo de 0<t<1.5
print(f'Termina {threading.current_thread().name}')
# Crear los hilos
hilo1 = threading.Thread(target=ejecutar, name='Hilo 1')
hilo2 = threading.Thread(target=ejecutar, name='Hilo 2')
hilo3 = threading.Thread(target=ejecutar, name='Hilo 3')
# Ejecutar los hilos
hilo1.start()
hilo2.start()
hilo3.start()
# Esperar a que terminen los hilos ejecutados
hilo1.join()
hilo2.join()
hilo3.join()
print('El hilo principal sí espera por el resto de hilos.')
Comienza Hilo 1
Comienza Hilo 2
Comienza Hilo 3
Termina Hilo 3
Termina Hilo 1
Termina Hilo 2
El hilo principal sí espera por el resto de hilos.
¿Cómo crear hilos, ejecutarlos y que el principal no espere?#
import threading # Manejo de hilos
from time import sleep # Pausar tiempos
import random # Números aleatorios
def ejecutar():
print(f'Comienza {threading.current_thread().name}')
sleep(1.5*random.random()) #Tiempo de 0<t<1.5
print(f'Termina {threading.current_thread().name}')
# Crear los hilos
hilo1 = threading.Thread(target=ejecutar, name='Hilo 1')
hilo2 = threading.Thread(target=ejecutar, name='Hilo 2')
hilo3 = threading.Thread(target=ejecutar, name='Hilo 3')
# Ejecutar los hilos
hilo1.start()
hilo2.start()
hilo3.start()
# Esperar a que terminen los hilos ejecutados
#hilo1.join()
#hilo2.join()
#hilo3.join()
print('\nEl hilo principal no espera por el resto de hilos.\n')
Comienza Hilo 1
Comienza Hilo 2
Comienza Hilo 3
El hilo principal no espera por el resto de hilos.
Termina Hilo 2
Termina Hilo 1
Termina Hilo 3
¿Cómo implemento un hilo en una subclase?#
Definir una nueva subclase de la clase Thread
Llamar al constructor de la clase Thread es obligatorio; usándolo, podemos redefinir algunas propiedades del hilo como el nombre del hilo.
Sobrecargue el método init_(self [,args]) para agregar argumentos adicionales
Luego, debo sobrecargar el método run(self [,args]) para implementar lo que debe hacer el subproceso cuando se inicia
En el programa principal, creamos varios objetos del tipo myThread; la ejecución del hilo comienza cuando se llama al método start().
El subproceso se coloca en el estado activo de la llamada a start() y permanece allí hasta que finaliza el método run() o lanza una excepción no controlada.
El programa finaliza cuando todos los subprocesos finalizan.
Caso 1#
import threading
import time
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print ("Iniciando " + self.name)
print_time(self.name, 5,self.counter)
print ("Finalizando " + self.name)
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print ("%s: %s\n" %(threadName, time.strftime("%H:%M:%S", time.localtime(time.time())))) # Thread-#: HH:MM:SS
counter -= 1
# Crear hilos
thread1 = myThread(1, "Thread-1", 1) # Objetos del tipo myThread # Hilo que muestra la hora cada 1 segundo
thread2 = myThread(2, "Thread-2", 4) # Objetos del tipo myThread # Hilo que muestra la hora cada 4 segundos
# Iniciar los hilos
thread1.start() #la ejecución del hilo comienza cuando se llama al método start().
thread2.start() #la ejecución del hilo comienza cuando se llama al método start().
Iniciando Thread-1
Iniciando Thread-2
Thread-1: 08:29:38
Finalizando Thread-1
Thread-2: 08:29:38
Thread-2: 08:29:43
Thread-2: 08:29:48
Thread-2: 08:29:53
Finalizando Thread-2
Caso 2#
import threading
import time
class myThread (threading.Thread):
def __init__(self, threadID, name, delay):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.delay = delay
def run(self):
print ("Iniciando " + self.name)
print_time(self.name, self.delay,5)
print ("Finalizando " + self.name)
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print ("%s: %s\n" %(threadName, time.strftime("%H:%M:%S", time.localtime(time.time())))) # Thread-#: HH:MM:SS
counter -= 1
# Crear hilos
thread1 = myThread(1, "Thread-1", 1) # Objetos del tipo myThread # Hilo que muestra la hora cada 1 segundo
thread2 = myThread(2, "Thread-2", 3) # Objetos del tipo myThread # Hilo que muestra la hora cada 4 segundos
# Iniciar los hilos
thread1.start() #la ejecución del hilo comienza cuando se llama al método start().
thread2.start() #la ejecución del hilo comienza cuando se llama al método start().
Iniciando Thread-1
Iniciando Thread-2
Thread-1: 08:32:16
Thread-1: 08:32:17
Thread-2: 08:32:18
Thread-1: 08:32:18
Thread-1: 08:32:19
Thread-1: 08:32:20
Finalizando Thread-1
Thread-2: 08:32:21
Thread-2: 08:32:24
Thread-2: 08:32:27
Thread-2: 08:32:30
Finalizando Thread-2
¿Hay que tomar precauciones al utilizar los hilos?#
Mientras el programa principal ha llegado al final, el hilo continúa imprimiendo su mensaje cada dos segundos.
Este ejemplo demuestra qué son los subprocesos: una subtarea que hace algo en un proceso principal.
Un punto clave a tener en cuenta al usar subprocesos es que siempre debe asegurarse de nunca dejar ningún subproceso ejecutándose en segundo plano. Esta es una programación muy mala y puede causarle todo tipo de problemas cuando trabaja en aplicaciones más grandes.
from threading import Thread
from time import sleep
class CookBook(Thread):
def __init__(self):
Thread.__init__(self)
self.message = "Hilo Trabajando!\n"
def print_message(self):
print (self.message)
def run(self):
print ("Hilo Inicia\n")
x=0
while (x < 3):
self.print_message()
sleep(2)
x += 1
print ("Hilo Finaliza\n")
print('\nPrograma principal inicia.\n')
hello_Python = CookBook()
hello_Python.start()
print('\nPrograma principal finaliza.\n')
Programa principal inicia.
Hilo Inicia
Hilo Trabajando!
Programa principal finaliza.
Hilo Trabajando!
Hilo Trabajando!
Hilo Finaliza
¿Cómo hago para detener a los hilos?#
Iniciar mis hilos#
import threading
import time
import numpy as np
# Variable global para indicar a los hilos que deben detenerse
global exit_flag
exit_flag = False
# Función que ejecutará cada hilo
def thread_function(thread_num):
while not exit_flag:
# Es aquí donde puedes poner el código que hace el trabajo del hilo
print(f"Hola, soy el hilo {thread_num}")
time.sleep(2*np.random.rand())
pass
print(f"Hilo {thread_num} detenido")
# Crear los hilos
threads = []
for i in range(2):
t = threading.Thread(target=thread_function, args=(i,))
threads.append(t)
# Iniciar los hilos
for t in threads:
t.start()
Hola, soy el hilo 0
Hola, soy el hilo 1
Hola, soy el hilo 0
Hola, soy el hilo 0
Hola, soy el hilo 1
Hola, soy el hilo 0
Hola, soy el hilo 0
Hola, soy el hilo 0
Hola, soy el hilo 1
Hola, soy el hilo 0
Hola, soy el hilo 0
Hola, soy el hilo 1
Hola, soy el hilo 0
Hola, soy el hilo 1
Hola, soy el hilo 0
Hola, soy el hilo 1
Hola, soy el hilo 0
Hola, soy el hilo 1
Hola, soy el hilo 0
Hola, soy el hilo 1
Hola, soy el hilo 0
Hola, soy el hilo 0
Hola, soy el hilo 1
Hola, soy el hilo 1
Hola, soy el hilo 0
Hola, soy el hilo 1
Detener mis hilos#
# Detener todos los hilos
exit_flag = True
Hilo 1 detenido
Hilo 0 detenido
¿Sumar matrices por filas? Veamoslo#
Realizaré la suma de las filas de tres matrices, cada una con cuatro filas y diez columnas. Tengo a mi disposición cuatro hilos de procesamiento y utilizaré un hilo para procesar cada conjunto de filas de las tres matrices. El proceso de suma de filas tarda aproximadamente 2 segundos en ejecutarse.
¿Hilo por hilo? 😒#
# Librerias que utilizaremos
import threading # Manejo de hilos
import numpy as np # Manejo de matrices
import time # Manejo de tiempos
# Definir las 3 matrices de 4x10, con números enteros entre 0 y 10
matrix1 = np.round(10*np.random.rand(4, 10)).astype(np.int8)
matrix2 = np.round(10*np.random.rand(4, 10)).astype(np.int8)
matrix3 = np.round(10*np.random.rand(4, 10)).astype(np.int8)
# Definir una matriz resultado de 4x10 con todos los valores en cero
result_matrix = np.zeros((4, 10)).astype(np.int8)
# Definir una función para sumar las filas de las matrices
def sum_rows(row_idx):
global result_matrix
result_matrix[row_idx] = matrix1[row_idx] + matrix2[row_idx] + matrix3[row_idx]
time.sleep(2) # Se demora 2 segundos sumando una fila
# Iniciar medición del tiempo
inicio = time.time()
# Crear 4 hilos, uno para cada fila
hilo1 = threading.Thread(target=sum_rows, args=(0,)) # Suma fila 1
hilo2 = threading.Thread(target=sum_rows, args=(1,)) # Suma fila 2
hilo3 = threading.Thread(target=sum_rows, args=(2,)) # Suma fila 3
hilo4 = threading.Thread(target=sum_rows, args=(3,)) # Suma fila 4
hilo1.start() # Poner a trabajar hilo 1
hilo1.join() # Esperar a que el hilo 1 termine
hilo2.start() # Poner a trabajar hilo 2
hilo2.join() # Esperar a que el hilo 2 termine
hilo3.start() # Poner a trabajar hilo 3
hilo3.join() # Esperar a que el hilo 3 termine
hilo4.start() # Poner a trabajar hilo 4
hilo4.join() # Esperar a que el hilo 4 termine
# Imprimir las matrices y la matriz resultado
print("Matrix1:\n", matrix1)
print("Matrix2:\n", matrix2)
print("Matrix3:\n", matrix3)
print("Result Matrix:\n", result_matrix)
# Mostrar tiempo requerido
fin = time.time()
tiempo_total = fin - inicio
print(tiempo_total,"segundos")
Matrix1:
[[3 7 2 4 7 8 8 4 4 5]
[7 0 7 7 4 5 6 2 1 8]
[3 3 3 3 3 1 7 5 6 4]
[8 3 8 1 0 9 6 0 1 9]]
Matrix2:
[[ 2 1 5 2 10 6 1 5 4 2]
[ 4 0 5 5 5 9 6 3 6 8]
[ 5 10 6 7 7 4 1 8 6 3]
[ 6 8 3 1 3 9 8 0 7 6]]
Matrix3:
[[ 0 2 3 9 6 10 6 10 10 5]
[10 0 4 8 7 10 7 4 2 8]
[ 3 0 6 6 9 5 5 9 5 0]
[ 1 6 6 8 4 2 5 5 8 1]]
Result Matrix:
[[ 5 10 10 15 23 24 15 19 18 12]
[21 0 16 20 16 24 19 9 9 24]
[11 13 15 16 19 10 13 22 17 7]
[15 17 17 10 7 20 19 5 16 16]]
8.04371976852417 segundos
¿Todos los hilos al tiempo? 😃#
# Librerias que utilizaremos
import threading # Manejo de hilos
import numpy as np # Manejo de matrices
import time # Manejo de tiempos
# Definir las 3 matrices de 4x10, con números enteros entre 0 y 10
matrix1 = np.round(10*np.random.rand(4, 10)).astype(np.int8)
matrix2 = np.round(10*np.random.rand(4, 10)).astype(np.int8)
matrix3 = np.round(10*np.random.rand(4, 10)).astype(np.int8)
# Definir una matriz resultado de 4x10 con todos los valores en cero
result_matrix = np.zeros((4, 10)).astype(np.int8)
# Definir una función para sumar las filas de las matrices
def sum_rows(row_idx):
global result_matrix
result_matrix[row_idx] = matrix1[row_idx] + matrix2[row_idx] + matrix3[row_idx]
time.sleep(2) # Se demora 2 segundos sumando una fila
# Iniciar medición del tiempo
inicio = time.time()
# Crear 4 hilos, uno para cada fila
hilo1 = threading.Thread(target=sum_rows, args=(0,)) # Suma fila 1
hilo2 = threading.Thread(target=sum_rows, args=(1,)) # Suma fila 2
hilo3 = threading.Thread(target=sum_rows, args=(2,)) # Suma fila 3
hilo4 = threading.Thread(target=sum_rows, args=(3,)) # Suma fila 4
# Poner a trabajar a los hilos
hilo1.start()
hilo2.start()
hilo3.start()
hilo4.start()
# Esperar a que todos los hilos terminen
hilo1.join()
hilo2.join()
hilo3.join()
hilo4.join()
# Imprimir las matrices y la matriz resultado
print("Matrix1:\n", matrix1)
print("Matrix2:\n", matrix2)
print("Matrix3:\n", matrix3)
print("Result Matrix:\n", result_matrix)
# Mostrar tiempo requerido
fin = time.time()
tiempo_total = fin - inicio
print(tiempo_total,"segundos")
Matrix1:
[[ 5 1 7 2 7 6 2 4 4 9]
[ 3 10 1 0 3 7 2 9 8 9]
[ 5 2 1 0 4 8 2 8 4 4]
[ 9 6 4 9 1 10 4 1 3 0]]
Matrix2:
[[ 5 4 3 5 3 9 7 2 0 0]
[ 1 3 9 3 10 8 3 0 8 6]
[ 7 3 10 4 3 8 0 8 10 6]
[ 8 5 7 0 10 6 10 7 8 10]]
Matrix3:
[[10 2 6 8 8 3 3 6 2 6]
[ 6 2 10 1 4 1 0 7 9 0]
[ 6 7 9 5 0 2 1 2 1 6]
[ 5 7 10 9 4 6 2 9 1 4]]
Result Matrix:
[[20 7 16 15 18 18 12 12 6 15]
[10 15 20 4 17 16 5 16 25 15]
[18 12 20 9 7 18 3 18 15 16]
[22 18 21 18 15 22 16 17 12 14]]
2.02053165435791 segundos
¿Y si sólo tengo 2 hilos? 😵💫#
# Librerias que utilizaremos
import threading # Manejo de hilos
import numpy as np # Manejo de matrices
import time # Manejo de tiempos
# Definir las 3 matrices de 4x10, con números enteros entre 0 y 10
matrix1 = np.round(10*np.random.rand(4, 10)).astype(np.int8)
matrix2 = np.round(10*np.random.rand(4, 10)).astype(np.int8)
matrix3 = np.round(10*np.random.rand(4, 10)).astype(np.int8)
# Definir una matriz resultado de 4x10 con todos los valores en cero
result_matrix = np.zeros((4, 10)).astype(np.int8)
# Definir una función para sumar las filas de las matrices
def sum_rows(row_idx):
global result_matrix
result_matrix[row_idx] = matrix1[row_idx] + matrix2[row_idx] + matrix3[row_idx]
time.sleep(2) # Se demora 2 segundos sumando una fila
# Iniciar medición del tiempo
inicio = time.time()
# Crear 2 hilos, uno para cada fila
hilo1 = threading.Thread(target=sum_rows, args=(0,)) # Suma fila 1
hilo2 = threading.Thread(target=sum_rows, args=(1,)) # Suma fila 2
# Poner a trabajar a los hilos
hilo1.start()
hilo2.start()
# Esperar a que todos los hilos terminen
hilo1.join()
hilo2.join()
# Crear 2 hilos, uno para cada fila
hilo1 = threading.Thread(target=sum_rows, args=(2,)) # Suma fila 2
hilo2 = threading.Thread(target=sum_rows, args=(3,)) # Suma fila 3
# Poner a trabajar a los hilos
hilo1.start()
hilo2.start()
# Esperar a que todos los hilos terminen
hilo1.join()
hilo2.join()
# Imprimir las matrices y la matriz resultado
print("Matrix1:\n", matrix1)
print("Matrix2:\n", matrix2)
print("Matrix3:\n", matrix3)
print("Result Matrix:\n", result_matrix)
# Mostrar tiempo requerido
fin = time.time()
tiempo_total = fin - inicio
print(tiempo_total,"segundos")
Matrix1:
[[ 0 4 3 2 10 10 3 2 1 6]
[ 2 3 10 7 7 6 1 2 7 2]
[ 2 9 10 5 4 7 3 5 4 8]
[ 4 9 10 7 8 7 7 3 4 2]]
Matrix2:
[[ 2 4 6 6 3 2 6 4 6 8]
[ 1 4 4 3 3 9 7 9 3 0]
[ 7 3 7 5 4 9 0 4 0 7]
[10 1 4 10 8 6 2 4 7 1]]
Matrix3:
[[ 4 7 6 6 2 6 5 4 3 6]
[ 7 10 2 8 9 2 2 10 2 7]
[ 3 10 0 5 0 0 9 4 1 3]
[ 2 7 4 5 6 9 6 9 3 1]]
Result Matrix:
[[ 6 15 15 14 15 18 14 10 10 20]
[10 17 16 18 19 17 10 21 12 9]
[12 22 17 15 8 16 12 13 5 18]
[16 17 18 22 22 22 15 16 14 4]]
4.024723529815674 segundos