Un hilo puede que tenga que terminar con su última instrucción pero no siempre tiene que ser así, ya hemos visto que hay otros que están pensados para ser ejecutados indefinidamente (daemon) otros también pueden estar continuamente a la espera de algo, por ejemplo una operación de e/s.
Cuando todos los hilos (no daemon) finalizan se finalizará el proceso en conjunto.
En algunos lenguajes podemos hacer que un hilo invoque la finalización de otro mediante abort o cancel. Python no tiene estas instrucciones y es labor del programador buscar un mecanismo para obtener el mismo resultado. Para manejar excepciones en hilos (threads) en Python y detener su ejecución, existen diversas técnicas y estrategias, ya que los hilos de Python tienen ciertas limitaciones (no pueden ser forzados a detenerse externamente).
Para capturar excepciones que ocurren en un hilo, se debe envolver el código del hilo en un bloque try-except. Este manejo es interno al hilo, y solo dentro de él se puede detectar y responder a una excepción que ocurre durante su ejecución.
import threading
def thread_function():
try:
# Simula el trabajo del hilo que puede generar una excepción
raise ValueError("Ocurrió un error en el hilo")
except Exception as e:
print(f"Excepción capturada en el hilo: {e}")
# Crear y ejecutar el hilo
thread = threading.Thread(target=thread_function)
thread.start()
thread.join()
En este ejemplo, el hilo ejecutará thread_function
y, si ocurre una excepción, la capturará e imprimirá un mensaje en lugar de que el programa principal falle abruptamente.
import threading
import queue
def thread_function(q):
try:
# Lógica del hilo aquí
raise ValueError("Ocurrió un error en el hilo.") # Ejemplo de excepción
except Exception as e:
q.put(e) # Almacena la excepción en la cola para el hilo principal
# Cola para capturar excepciones
exception_queue = queue.Queue()
# Crear y ejecutar el hilo
thread = threading.Thread(target=thread_function, args=(exception_queue,))
thread.start()
thread.join()
# Verificar si hubo excepciones
if not exception_queue.empty():
exception = exception_queue.get()
print(f"Excepción capturada desde el hilo: {exception}")
Explicación del código
queue.Queue
) para comunicar la excepción al hilo principal.thread_function
, cualquier excepción se captura y se coloca en la cola.thread.join()
), verificamos si hay excepciones en la cola, y de ser así, las manejamos.Python no ofrece una forma directa de abortar un hilo en ejecución por seguridad y diseño de su implementación de threading. Sin embargo, existen varias estrategias para implementar un sistema de “cancelación suave” de hilos. Una de las más comunes es utilizar un flag de parada (o indicador de detención), que el hilo verificará periódicamente para decidir si debe detenerse.
import threading
import time
class WorkerThread(threading.Thread):
def __init__(self):
super().__init__()
self._stop_event = threading.Event() # Evento de detención
def run(self):
print("Hilo iniciado")
while not self._stop_event.is_set(): # Revisa el flag periódicamente
print("Hilo en ejecución")
time.sleep(1) # Simula trabajo en curso
def stop(self):
self._stop_event.set() # Activa el flag para detener el hilo
# Crear y ejecutar el hilo
worker_thread = WorkerThread()
worker_thread.start()
# Ejecutar otros procesos o lógica principal
time.sleep(3)
# Detener el hilo
worker_thread.stop()
worker_thread.join()
print("Hilo detenido")
En este código:
_stop_event
, que actúa como un indicador de detención._stop_event
con is_set()
para ver si debe continuar o no._stop_event
, lo cual indica al hilo que debe terminar su ciclo.import threading
import time
# Evento para señalizar la parada del hilo
stop_event = threading.Event()
def thread_function():
while not stop_event.is_set(): # Mientras no esté activado, continúa
print("Hilo en ejecución...")
time.sleep(1) # Simulación de trabajo
print("Hilo detenido.")
# Crear y ejecutar el hilo
thread = threading.Thread(target=thread_function)
thread.start()
# Ejecutar por unos segundos y luego detener
time.sleep(5)
stop_event.set() # Señalizar al hilo para detenerse
thread.join() # Esperar a que el hilo termine
print("Hilo principal: El hilo ha terminado.")
Explicación del código
threading.Event
), que es un mecanismo seguro y eficiente para la comunicación entre hilos.stop_event.set()
desde el hilo principal, indicamos que el hilo debe detenerse en la próxima revisión del evento.Debido a que Python no permite la interrupción o terminación forzada de un hilo en ejecución (principalmente por razones de seguridad y estabilidad del intérprete), debemos implementar una lógica cooperativa para que el hilo «sepa» cuándo debe detenerse. Si un hilo está ejecutando una operación bloqueante (por ejemplo, espera de entrada/salida sin tiempo de espera), es más complicado forzar su detención; en esos casos es preferible diseñar la función del hilo para que revise con frecuencia el flag de detención o use una operación con timeout que permita el chequeo periódico del estado del flag.
import threading
import time
class HiloAbortable(threading.Thread):
def __init__(self):
super(HiloAbortable, self).__init__()
self._abort_event = threading.Event()
def abort(self):
self._abort_event.set()
def aborted(self):
return self._abort_event.is_set()
def run(self):
while (True):
if (self.aborted()):
print ("Hilo abortado")
break
#resto del trabajo a ejecutar
print ("trabajando...")
h =HiloAbortable()
h.start()
time.sleep(0.001)
print ("Antes de invocación a abort()")
h.abort()
print ("Después de invocación de abort()")
trabajando... trabajando... trabajando... trabajando... trabajando... trabajando... trabajando... trabajando... trabajando... trabajando... trabajando... trabajando... trabajando... trabajando... Antes de invocación a abort() trabajando... Después de invocación de abort() Hilo abortado