logo

Pitón | Diferentes formas de matar un hilo

En general, matar subprocesos abruptamente se considera una mala práctica de programación. Matar un hilo abruptamente podría dejar abierto un recurso crítico que debe cerrarse correctamente. Pero es posible que desees cerrar un hilo una vez que haya pasado un período de tiempo específico o se haya generado alguna interrupción. Existen varios métodos mediante los cuales puedes eliminar un hilo en Python.

  • Generar excepciones en un hilo de Python
  • Establecer/restablecer indicador de parada
  • Usar rastros para matar hilos
  • Usando el módulo de multiprocesamiento para matar hilos
  • Matar el hilo de Python configurándolo como demonio
  • Usando una función oculta _stop()

Generar excepciones en un hilo de Python:
Este método utiliza la función PyThreadState_SetAsyncExc() para generar una excepción en el hilo a. Por ejemplo,



Python3








# Python program raising> # exceptions in a python> # thread> import> threading> import> ctypes> import> time> > class> thread_with_exception(threading.Thread):> >def> __init__(>self>, name):> >threading.Thread.__init__(>self>)> >self>.name>=> name> > >def> run(>self>):> ># target function of the thread class> >try>:> >while> True>:> >print>(>'running '> +> self>.name)> >finally>:> >print>(>'ended'>)> > >def> get_id(>self>):> ># returns id of the respective thread> >if> hasattr>(>self>,>'_thread_id'>):> >return> self>._thread_id> >for> id>, thread>in> threading._active.items():> >if> thread>is> self>:> >return> id> > >def> raise_exception(>self>):> >thread_id>=> self>.get_id()> >res>=> ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,> >ctypes.py_object(SystemExit))> >if> res>>1>:> >ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,>0>)> >print>(>'Exception raise failure'>)> > t1>=> thread_with_exception(>'Thread 1'>)> t1.start()> time.sleep(>2>)> t1.raise_exception()> t1.join()>

métodos java

>

>

Cuando ejecutamos el código anterior en una máquina, notará que tan pronto como se llama a la función rise_exception(), la función de destino run() finaliza. Esto se debe a que tan pronto como se genera una excepción, el control del programa salta del bloque try y la función run() finaliza. Después de eso, se puede llamar a la función join() para finalizar el hilo. En ausencia de la función run_exception(), la función de destino run() sigue ejecutándose para siempre y la función join() nunca se llama para finalizar el hilo.

Establecer/restablecer indicador de parada:
Para cerrar un hilo, podemos declarar una bandera de detención y esta bandera será revisada ocasionalmente por el hilo. Por ejemplo

Python3




# Python program showing> # how to kill threads> # using set/reset stop> # flag> import> threading> import> time> def> run():> >while> True>:> >print>(>'thread running'>)> >global> stop_threads> >if> stop_threads:> >break> stop_threads>=> False> t1>=> threading.Thread(target>=> run)> t1.start()> time.sleep(>1>)> stop_threads>=> True> t1.join()> print>(>'thread killed'>)>

>

>

En el código anterior, tan pronto como se establece la variable global stop_threads, la función de destino run() finaliza y el hilo t1 se puede eliminar usando t1.join(). Pero uno puede abstenerse de utilizar la variable global por determinadas razones. Para esas situaciones, se pueden pasar objetos de función para proporcionar una funcionalidad similar a la que se muestra a continuación.

Python3




# Python program killing> # threads using stop> # flag> import> threading> import> time> def> run(stop):> >while> True>:> >print>(>'thread running'>)> >if> stop():> >break> > def> main():> >stop_threads>=> False> >t1>=> threading.Thread(target>=> run, args>=>(>lambda> : stop_threads, ))> >t1.start()> >time.sleep(>1>)> >stop_threads>=> True> >t1.join()> >print>(>'thread killed'>)> main()>

>

>

El objeto de función pasado en el código anterior siempre devuelve el valor de la variable local stop_threads. Este valor se verifica en la función run(), y tan pronto como se restablece stop_threads, la función run() finaliza y se puede eliminar el hilo.

Usando rastros para matar hilos:
Este método funciona instalando rastros en cada hilo. Cada rastro termina al detectar algún estímulo o señal, matando así instantáneamente el hilo asociado. Por ejemplo

Python3


lenguaje java central



# Python program using> # traces to kill threads> import> sys> import> trace> import> threading> import> time> class> thread_with_trace(threading.Thread):> >def> __init__(>self>,>*>args,>*>*>keywords):> >threading.Thread.__init__(>self>,>*>args,>*>*>keywords)> >self>.killed>=> False> >def> start(>self>):> >self>.__run_backup>=> self>.run> >self>.run>=> self>.__run> >threading.Thread.start(>self>)> >def> __run(>self>):> >sys.settrace(>self>.globaltrace)> >self>.__run_backup()> >self>.run>=> self>.__run_backup> >def> globaltrace(>self>, frame, event, arg):> >if> event>=>=> 'call'>:> >return> self>.localtrace> >else>:> >return> None> >def> localtrace(>self>, frame, event, arg):> >if> self>.killed:> >if> event>=>=> 'line'>:> >raise> SystemExit()> >return> self>.localtrace> >def> kill(>self>):> >self>.killed>=> True> def> func():> >while> True>:> >print>(>'thread running'>)> t1>=> thread_with_trace(target>=> func)> t1.start()> time.sleep(>2>)> t1.kill()> t1.join()> if> not> t1.isAlive():> >print>(>'thread killed'>)>

>

>

En este código, start() se modifica ligeramente para configurar la función de seguimiento del sistema usando trazar() . La función de seguimiento local se define de manera que, cada vez que se establece el indicador de eliminación (eliminado) del subproceso respectivo, se genera una excepción SystemExit al ejecutar la siguiente línea de código, que finaliza la ejecución de la función de destino. Ahora el hilo se puede eliminar con join().

Usando el módulo de multiprocesamiento para matar hilos:
El módulo de multiprocesamiento de Python le permite generar procesos de la misma manera que genera subprocesos utilizando el módulo de subprocesos. La interfaz del módulo de subprocesos múltiples es similar a la del módulo de subprocesos. Por ejemplo, en un código determinado creamos tres subprocesos (procesos) que cuentan del 1 al 9.

Python3




# Python program creating> # three threads> import> threading> import> time> # counts from 1 to 9> def> func(number):> >for> i>in> range>(>1>,>10>):> >time.sleep(>0.01>)> >print>(>'Thread '> +> str>(number)>+> ': prints '> +> str>(number>*>i))> # creates 3 threads> for> i>in> range>(>0>,>3>):> >thread>=> threading.Thread(target>=>func, args>=>(i,))> >thread.start()>

>

>

La funcionalidad del código anterior también se puede implementar utilizando el módulo de multiprocesamiento de manera similar, con muy pocos cambios. Consulte el código que se proporciona a continuación.

Python3




# Python program creating> # thread using multiprocessing> # module> import> multiprocessing> import> time> def> func(number):> >for> i>in> range>(>1>,>10>):> >time.sleep(>0.01>)> >print>(>'Processing '> +> str>(number)>+> ': prints '> +> str>(number>*>i))> for> i>in> range>(>0>,>3>):> >process>=> multiprocessing.Process(target>=>func, args>=>(i,))> >process.start()>

>

>

Aunque la interfaz de los dos módulos es similar, los dos módulos tienen implementaciones muy diferentes. Todos los hilos comparten variables globales, mientras que los procesos están completamente separados entre sí. Por lo tanto, eliminar procesos es mucho más seguro en comparación con eliminar subprocesos. La clase Process recibe un método, Terminar() , para matar un proceso. Ahora, volvamos al problema inicial. Supongamos que en el código anterior queremos eliminar todos los procesos después de que hayan pasado 0,03 segundos. Esta funcionalidad se logra utilizando el módulo de multiprocesamiento en el siguiente código.

Python3




# Python program killing> # a thread using multiprocessing> # module> import> multiprocessing> import> time> def> func(number):> >for> i>in> range>(>1>,>10>):> >time.sleep(>0.01>)> >print>(>'Processing '> +> str>(number)>+> ': prints '> +> str>(number>*>i))> # list of all processes, so that they can be killed afterwards> all_processes>=> []> for> i>in> range>(>0>,>3>):> >process>=> multiprocessing.Process(target>=>func, args>=>(i,))> >process.start()> >all_processes.append(process)> # kill all processes after 0.03s> time.sleep(>0.03>)> for> process>in> all_processes:> >process.terminate()>

>

>

Aunque los dos módulos tienen implementaciones diferentes. Esta funcionalidad proporcionada por el módulo de multiprocesamiento en el código anterior es similar a eliminar subprocesos. Por lo tanto, el módulo de multiprocesamiento se puede utilizar como un simple alternativa siempre que se nos solicite implementar la eliminación de subprocesos en Python.

Matar el hilo de Python configurándolo como demonio:
Hilos de demonio son aquellos subprocesos que mueren cuando sale el programa principal. Por ejemplo

Python3

composición de la relación




import> threading> import> time> import> sys> def> func():> >while> True>:> >time.sleep(>0.5>)> >print>(>'Thread alive, and it won't die on program termination'>)> t1>=> threading.Thread(target>=>func)> t1.start()> time.sleep(>2>)> sys.exit()>

>

>

Tenga en cuenta que el subproceso t1 permanece activo y evita que el programa principal salga mediante sys.exit(). En Python, cualquier subproceso vivo que no sea un demonio bloquea la salida del programa principal. Mientras que los subprocesos del demonio se eliminan tan pronto como se cierra el programa principal. En otras palabras, tan pronto como se cierra el programa principal, se eliminan todos los subprocesos del demonio. Para declarar un hilo como demonio, configuramos el argumento de palabra clave demonio como Verdadero. Por ejemplo, en el código proporcionado se demuestra la propiedad de los subprocesos del demonio.

Python3




# Python program killing> # thread using daemon> import> threading> import> time> import> sys> def> func():> >while> True>:> >time.sleep(>0.5>)> >print>(>'Thread alive, but it will die on program termination'>)> t1>=> threading.Thread(target>=>func)> t1.daemon>=> True> t1.start()> time.sleep(>2>)> sys.exit()>

>

>

Observe que, tan pronto como sale el programa principal, el hilo t1 se elimina. Este método resulta extremadamente útil en los casos en los que la terminación del programa se puede utilizar para provocar la eliminación de subprocesos. Tenga en cuenta que en Python, el programa principal finaliza tan pronto como todos los subprocesos que no son demonios están muertos, independientemente del número de subprocesos demonio vivos. Por lo tanto, es posible que los recursos retenidos por estos subprocesos de demonio, como archivos abiertos, transacciones de bases de datos, etc., no se liberen correctamente. El hilo de control inicial en un programa de Python no es un hilo de demonio. No se recomienda cerrar un hilo a la fuerza a menos que se sepa con seguridad que hacerlo no provocará fugas ni bloqueos.
Usando una función oculta _stop() :
Para cerrar un hilo, usamos la función oculta _stop(). Esta función no está documentada pero podría desaparecer en la próxima versión de Python.

lobo contra zorro

Python3




# Python program killing> # a thread using ._stop()> # function> import> time> import> threading> class> MyThread(threading.Thread):> ># Thread class with a _stop() method.> ># The thread itself has to check> ># regularly for the stopped() condition.> >def> __init__(>self>,>*>args,>*>*>kwargs):> >super>(MyThread,>self>).__init__(>*>args,>*>*>kwargs)> >self>._stop>=> threading.Event()> ># function using _stop function> >def> stop(>self>):> >self>._stop.>set>()> >def> stopped(>self>):> >return> self>._stop.isSet()> >def> run(>self>):> >while> True>:> >if> self>.stopped():> >return> >print>(>'Hello, world!'>)> >time.sleep(>1>)> t1>=> MyThread()> t1.start()> time.sleep(>5>)> t1.stop()> t1.join()>

>

>

Nota: Es posible que los métodos anteriores no funcionen en una situación u otra, porque Python no proporciona ningún método directo para eliminar subprocesos.