thread sincronizar raspberry multitarea hilos entre detener comunicacion como python multithreading kill terminate

raspberry - sincronizar hilos en python



¿Hay alguna manera de matar un hilo? (24)

¿Es posible terminar un subproceso en ejecución sin configurar / verificar ninguna bandera / semáforo / etc?


Esta es una mala respuesta, ver los comentarios.

Aquí está cómo hacerlo:

from threading import * ... for thread in enumerate(): if thread.isAlive(): try: thread._Thread__stop() except: print(str(thread.getName()) + '' could not be terminated''))

Dale unos segundos y luego tu hilo debe detenerse. Compruebe también el método thread._Thread__delete() .

Recomendaría un método thread.quit() por conveniencia. Por ejemplo, si tiene un socket en su hilo, le recomiendo crear un método quit() en su clase de socket-handle, terminar el socket y luego ejecutar un thread._Thread__stop() dentro de su quit() .


Como otros han mencionado, la norma es establecer una bandera de parada. Para algo ligero (sin subclase de Thread, sin variable global), una devolución de llamada lambda es una opción. (Note los paréntesis en if stop() .)

import threading import time def do_work(id, stop): print("I am thread", id) while True: print("I am thread {} doing something".format(id)) if stop(): print(" Exiting loop.") break print("Thread {}, signing off".format(id)) def main(): stop_threads = False workers = [] for id in range(0,3): tmp = threading.Thread(target=do_work, args=(id, lambda: stop_threads)) workers.append(tmp) tmp.start() time.sleep(3) print(''main: done sleeping; time to stop the threads.'') stop_threads = True for worker in workers: worker.join() print(''Finis.'') if __name__ == ''__main__'': main()

Reemplazar print() con una función pr() que siempre descarga ( sys.stdout.flush() ) puede mejorar la precisión de la salida del shell.

(Solo probado en Windows / Eclipse / Python3.3)


Definitivamente es posible implementar un método Thread.stop como se muestra en el siguiente código de ejemplo:

import sys import threading import time class StopThread(StopIteration): pass threading.SystemExit = SystemExit, StopThread class Thread2(threading.Thread): def stop(self): self.__stop = True def _bootstrap(self): if threading._trace_hook is not None: raise ValueError(''Cannot run thread with tracing!'') self.__stop = False sys.settrace(self.__trace) super()._bootstrap() def __trace(self, frame, event, arg): if self.__stop: raise StopThread() return self.__trace class Thread3(threading.Thread): def _bootstrap(self, stop_thread=False): def stop(): nonlocal stop_thread stop_thread = True self.stop = stop def tracer(*_): if stop_thread: raise StopThread() return tracer sys.settrace(tracer) super()._bootstrap() ############################################################################### def main(): test1 = Thread2(target=printer) test1.start() time.sleep(1) test1.stop() test1.join() test2 = Thread2(target=speed_test) test2.start() time.sleep(1) test2.stop() test2.join() test3 = Thread3(target=speed_test) test3.start() time.sleep(1) test3.stop() test3.join() def printer(): while True: print(time.time() % 1) time.sleep(0.1) def speed_test(count=0): try: while True: count += 1 except StopThread: print(''Count ='', count) if __name__ == ''__main__'': main()

La clase Thread3 parece ejecutar el código aproximadamente un 33% más rápido que la clase Thread2 .


En Python, simplemente no puedes matar un hilo directamente.

Si realmente NO necesita tener un subproceso (!), Lo que puede hacer, en lugar de usar el paquete de subprocesos , es usar el paquete de multiprocesamiento . Aquí, para matar un proceso, simplemente puede llamar al método:

yourProcess.terminate() # kill the process!

Python matará su proceso (en Unix a través de la señal SIGTERM, mientras que en Windows a través de la llamada TerminateProcess() ). ¡Presta atención para usarlo mientras usas una Cola o una Tubería! (Puede corromper los datos en la Cola / Tubería)

Tenga en cuenta que el multiprocessing.Event y el multiprocessing.Semaphore funcionan exactamente de la misma manera que el threading.Event y el threading.Semaphore respectivamente. De hecho, los primeros son clones de los últimos.

Si REALMENTE necesitas usar un hilo, no hay forma de matarlo directamente. Sin embargo, lo que puede hacer es utilizar un "subproceso de daemon" . De hecho, en Python, un hilo puede marcarse como demonio :

yourThread.daemon = True # set the Thread as a "daemon thread"

El programa principal se cerrará cuando no queden hilos vivos que no sean de demonio. En otras palabras, cuando su subproceso principal (que es, por supuesto, un subproceso no daemon) finalice sus operaciones, el programa se cerrará incluso si todavía hay algunos subprocesos de daemon en funcionamiento.

Tenga en cuenta que es necesario establecer un subproceso como daemon antes de llamar al método start() .

Por supuesto que puedes, y deberías, usar el daemon incluso con multiprocessing . Aquí, cuando el proceso principal sale, intenta terminar todos sus procesos secundarios demoníacos.

Finalmente, tenga en cuenta que sys.exit() y os.kill() no son opciones.


Es mejor si no matas un hilo. Una forma podría ser introducir un bloque de "prueba" en el ciclo del hilo y lanzar una excepción cuando desee detener el hilo (por ejemplo, un corte / retorno / ... que detiene su para / while / ...). He usado esto en mi aplicación y funciona ...


Esto parece funcionar con pywin32 en windows 7

my_thread = threading.Thread() my_thread.start() my_thread._Thread__stop()


Esto se basa en thread2 - hilos killables (receta de Python)

Debe llamar a PyThreadState_SetasyncExc (), que solo está disponible a través de ctypes.

Esto solo se ha probado en Python 2.7.3, pero es probable que funcione con otras versiones recientes de la versión 2.x.

import ctypes def terminate_thread(thread): """Terminates a python thread from another thread. :param thread: a threading.Thread instance """ if not thread.isAlive(): return exc = ctypes.py_object(SystemExit) res = ctypes.pythonapi.PyThreadState_SetAsyncExc( ctypes.c_long(thread.ident), exc) if res == 0: raise ValueError("nonexistent thread id") elif res > 1: # """if it returns a number greater than one, you''re in trouble, # and you should call it again with exc=NULL to revert the effect""" ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None) raise SystemError("PyThreadState_SetAsyncExc failed")


Generalmente es un mal patrón matar un hilo de forma abrupta, en Python y en cualquier idioma. Piense en los siguientes casos:

  • el hilo contiene un recurso crítico que debe cerrarse correctamente
  • el hilo ha creado varios otros hilos que también deben eliminarse.

La buena manera de manejar esto si puede permitírselo (si está administrando sus propios subprocesos) es tener un indicador de exit_request que verifique cada subproceso en intervalos regulares para ver si es el momento de salir.

Por ejemplo:

import threading class StoppableThread(threading.Thread): """Thread class with a stop() method. The thread itself has to check regularly for the stopped() condition.""" def __init__(self): super(StoppableThread, self).__init__() self._stop_event = threading.Event() def stop(self): self._stop_event.set() def stopped(self): return self._stop_event.is_set()

En este código, debe llamar a stop () en el subproceso cuando desee que salga, y esperar a que el subproceso salga correctamente utilizando join (). El hilo debe comprobar la bandera de parada a intervalos regulares.

Sin embargo, hay casos en los que realmente necesitas matar un hilo. Un ejemplo es cuando está envolviendo una biblioteca externa que está ocupada para llamadas largas y desea interrumpirla.

El siguiente código permite (con algunas restricciones) generar una excepción en un hilo de Python:

def _async_raise(tid, exctype): ''''''Raises an exception in the threads with id tid'''''' if not inspect.isclass(exctype): raise TypeError("Only types can be raised (not instances)") res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), ctypes.py_object(exctype)) if res == 0: raise ValueError("invalid thread id") elif res != 1: # "if it returns a number greater than one, you''re in trouble, # and you should call it again with exc=NULL to revert the effect" ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None) raise SystemError("PyThreadState_SetAsyncExc failed") class ThreadWithExc(threading.Thread): ''''''A thread class that supports raising exception in the thread from another thread. '''''' def _get_my_tid(self): """determines this (self''s) thread id CAREFUL : this function is executed in the context of the caller thread, to get the identity of the thread represented by this instance. """ if not self.isAlive(): raise threading.ThreadError("the thread is not active") # do we have it cached? if hasattr(self, "_thread_id"): return self._thread_id # no, look for it in the _active dict for tid, tobj in threading._active.items(): if tobj is self: self._thread_id = tid return tid # TODO: in python 2.6, there''s a simpler way to do : self.ident raise AssertionError("could not determine the thread''s id") def raiseExc(self, exctype): """Raises the given exception type in the context of this thread. If the thread is busy in a system call (time.sleep(), socket.accept(), ...), the exception is simply ignored. If you are sure that your exception should terminate the thread, one way to ensure that it works is: t = ThreadWithExc( ... ) ... t.raiseExc( SomeException ) while t.isAlive(): time.sleep( 0.1 ) t.raiseExc( SomeException ) If the exception is to be caught by the thread, you need a way to check that your thread has caught it. CAREFUL : this function is executed in the context of the caller thread, to raise an excpetion in the context of the thread represented by this instance. """ _async_raise( self._get_my_tid(), exctype )

(Basado en los subprocesos Killable por Tomer Filiba. La cita sobre el valor de retorno de PyThreadState_SetAsyncExc parece ser de una versión anterior de Python ).

Como se señala en la documentación, esto no es una bala mágica porque si el hilo está ocupado fuera del intérprete de Python, no detectará la interrupción.

Un buen patrón de uso de este código es hacer que el subproceso capte una excepción específica y realice la limpieza. De esa manera, puede interrumpir una tarea y aun así tener la limpieza adecuada.


Hay una biblioteca construida para este propósito, stopit . Aunque algunas de las precauciones enumeradas en este documento siguen vigentes, al menos esta biblioteca presenta una técnica regular y repetible para lograr el objetivo establecido.


Inicie el subproceso con setDaemon (True).

def bootstrap(_filename): mb = ModelBootstrap(filename=_filename) # Has many Daemon threads. All get stopped automatically when main thread is stopped. t = threading.Thread(target=bootstrap,args=(''models.conf'',)) t.setDaemon(False) while True: t.start() time.sleep(10) # I am just allowing the sub-thread to run for 10 sec. You can listen on an event to stop execution. print(''Thread stopped'') break


La siguiente solución puede usarse para matar un hilo:

kill_threads = False def doSomething(): global kill_threads while True: if kill_threads: thread.exit() ...... ...... thread.start_new_thread(doSomething, ())

Esto se puede usar incluso para terminar hilos, cuyo código está escrito en otro módulo, desde el hilo principal. Podemos declarar una variable global en ese módulo y usarla para terminar los subprocesos generados en ese módulo.

Usualmente uso esto para terminar todos los hilos en la salida del programa. Esta podría no ser la manera perfecta de terminar un / a hilo / s, pero podría ayudar.


Llego tarde a este juego, pero he estado luchando con una pregunta similar y lo siguiente parece resolver el problema a la perfección para mí Y me permite hacer una comprobación y limpieza del estado del subproceso básico cuando el subproceso del daemonized se cierra:

import threading import time import atexit def do_work(): i = 0 @atexit.register def goodbye(): print ("''CLEANLY'' kill sub-thread with value: %s [THREAD: %s]" % (i, threading.currentThread().ident)) while True: print i i += 1 time.sleep(1) t = threading.Thread(target=do_work) t.daemon = True t.start() def after_timeout(): print "KILL MAIN THREAD: %s" % threading.currentThread().ident raise SystemExit threading.Timer(2, after_timeout).start()

Rendimientos:

0 1 KILL MAIN THREAD: 140013208254208 ''CLEANLY'' kill sub-thread with value: 2 [THREAD: 140013674317568]


No hay una API oficial para hacer eso, no.

Debe usar la API de la plataforma para eliminar el hilo, por ejemplo, pthread_kill o TerminateThread. Puede acceder a dicha API, por ejemplo, a través de pythonwin o a través de ctypes.

Tenga en cuenta que esto es inherentemente inseguro. Es probable que genere basura no recolectable (de las variables locales de los marcos de la pila que se convierten en basura), y puede llevar a puntos muertos, si el hilo que se está matando tiene la GIL en el punto en que se mata.


Nunca debes matar un hilo por la fuerza sin cooperar con él.

Matar un hilo elimina cualquier garantía de que los bloqueos de prueba / finalmente se configuren para que pueda dejar los bloqueos bloqueados, los archivos abiertos, etc.

El único momento en que se puede argumentar que matar hilos por la fuerza es una buena idea es matar un programa rápidamente, pero nunca solo hilos.


Pieter Hintjens, uno de los fundadores del proyecto ØMQ , dice que usar ØMQ y evitar primitivas de sincronización como bloqueos, exclusión mutua, eventos, etc. es la forma más segura y segura de escribir programas de múltiples hilos:

http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ

Esto incluye decirle a un hilo hijo, que debe cancelar su trabajo. Esto se haría equipando el hilo con un zócalo ØMQ y sondeando en ese zócalo un mensaje que indique que debería cancelarse.

El enlace también proporciona un ejemplo de código de Python con subprocesos múltiples con ØMQ.


Puede ejecutar su comando en un proceso y luego eliminarlo usando la ID de proceso. Necesitaba sincronizar entre dos hilos, uno de los cuales no vuelve por sí mismo.

processIds = [] def executeRecord(command): print(command) process = subprocess.Popen(command, stdout=subprocess.PIPE) processIds.append(process.pid) print(processIds[0]) #Command that doesn''t return by itself process.stdout.read().decode("utf-8") return; def recordThread(command, timeOut): thread = Thread(target=executeRecord, args=(command,)) thread.start() thread.join(timeOut) os.kill(processIds.pop(), signal.SIGINT) return;


Puedes matar un hilo instalando el rastreo en el hilo que saldrá del hilo. Vea el enlace adjunto para una posible implementación.

Mata un hilo en Python


Si bien es bastante antiguo, esta podría ser una solución útil para algunos:

Un pequeño módulo que amplía la funcionalidad del módulo de subprocesos: permite que un subproceso genere excepciones en el contexto de otro subproceso. Al elevar SystemExit , finalmente puedes matar los hilos de python.

import threading import ctypes def _async_raise(tid, excobj): res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj)) if res == 0: raise ValueError("nonexistent thread id") elif res > 1: # """if it returns a number greater than one, you''re in trouble, # and you should call it again with exc=NULL to revert the effect""" ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) raise SystemError("PyThreadState_SetAsyncExc failed") class Thread(threading.Thread): def raise_exc(self, excobj): assert self.isAlive(), "thread must be started" for tid, tobj in threading._active.items(): if tobj is self: _async_raise(tid, excobj) return # the thread was alive when we entered the loop, but was not found # in the dict, hence it must have been already terminated. should we raise # an exception here? silently ignore? def terminate(self): # must raise the SystemExit type, instead of a SystemExit() instance # due to a bug in PyThreadState_SetAsyncExc self.raise_exc(SystemExit)

Por lo tanto, permite un "subproceso para generar excepciones en el contexto de otro subproceso" y de esta manera, el subproceso terminado puede manejar la terminación sin verificar regularmente un indicador de cancelación.

Sin embargo, según su fuente original , hay algunos problemas con este código.

  • La excepción se generará solo cuando se ejecute el bytecode de Python. Si su hilo llama a una función de bloqueo nativa / incorporada, la excepción se generará solo cuando la ejecución vuelva al código de python.
    • También hay un problema si la función incorporada llama a PyErr_Clear () internamente, lo que cancelaría efectivamente su excepción pendiente. Puedes intentar levantarlo de nuevo.
  • Sólo los tipos de excepción se pueden elevar de forma segura. Es probable que las instancias de excepción causen un comportamiento inesperado, y por lo tanto están restringidas.
  • Pedí que se expusiera esta función en el módulo de subproceso integrado, pero como ctypes se ha convertido en una biblioteca estándar (a partir de 2.5), y esto
    no es probable que la característica sea agnóstica en la implementación, se puede mantener
    no expuesto

Si está intentando terminar todo el programa, puede establecer el hilo como un "demonio". ver Thread.daemon


Si está llamando explícitamente a time.sleep() como parte de su hilo (por ejemplo, encuestando algún servicio externo), una mejora en el método de Phillipe es usar el tiempo de espera en el método de wait() del event donde quiera que esté sleep()

Por ejemplo:

import threading class KillableThread(threading.Thread): def __init__(self, sleep_interval=1): super().__init__() self._kill = threading.Event() self._interval = sleep_interval def run(self): while True: print("Do Something") # If no kill signal is set, sleep for the interval, # If kill signal comes in while sleeping, immediately # wake up and handle is_killed = self._kill.wait(self._interval) if is_killed: break print("Killing Thread") def kill(self): self._kill.set()

Entonces a ejecutarlo

t = KillableThread(sleep_interval=5) t.start() # Every 5 seconds it prints: #: Do Something t.kill() #: Killing Thread

La ventaja de usar wait() lugar de sleep() ing y controlar regularmente el evento es que puede programar en intervalos más prolongados de sleep, el hilo se detiene casi inmediatamente (cuando de lo contrario estaría en sleep() ing) y en mi opinión , el código para manejar la salida es significativamente más simple.


Si realmente necesita la capacidad de eliminar una subtarea, use una implementación alternativa. multiprocessing y gevent soportan la eliminación indiscriminada de un "hilo".

El enhebrado de Python no admite la cancelación. Ni lo intentes. Es muy probable que su código se bloquee, corrompa o pierda memoria, o que tenga otros efectos "interesantes" difíciles de depurar no intencionados que ocurren rara vez y de manera no determinista.


Un proceso de multiprocessing.Process puede p.terminate()

En los casos en los que quiero eliminar un hilo, pero no quiero usar indicadores / bloqueos / señales / semáforos / eventos / lo que sea, promuevo los hilos a procesos completos. Para el código que utiliza solo algunos hilos, la sobrecarga no es tan mala.

Por ejemplo, esto es útil para terminar fácilmente los "hilos" auxiliares que ejecutan el bloqueo de E / S

La conversión es trivial: en el código relacionado, reemplace todos los threading.Thread queue.Queue con multiprocessing.Process y all queue.Queue con multiprocessing.Queue y agregue las llamadas requeridas de p.terminate() a su proceso principal que quiere matar a su p secundario

Documento de Python


Una cosa que quiero agregar es que si lee la documentación oficial en threading lib Python , se recomienda evitar el uso de hilos "demoníacos", cuando no quiera que los hilos terminen abruptamente, con la bandera que share Paolo Rovelli.

De la documentación oficial:

Los hilos del demonio se detienen abruptamente en el cierre. Es posible que sus recursos (como archivos abiertos, transacciones de base de datos, etc.) no se publiquen correctamente. Si desea que sus hilos se detengan correctamente, conviértalos en no demoníacos y utilice un mecanismo de señalización adecuado, como un Evento.

Creo que crear hilos demoníacos depende de su aplicación, pero en general (y en mi opinión) es mejor evitar matarlos o hacerlos demoníacos. En el multiproceso puede usar is_alive() para verificar el estado del proceso y "terminar" para finalizarlos (también evita los problemas de GIL). Pero puede encontrar más problemas, a veces, cuando ejecuta su código en Windows.

Y siempre recuerde que si tiene "hilos en vivo", el intérprete de Python se ejecutará para esperarlos. (Debido a esto, la daemónica puede ayudarte si no importa, termina abruptamente).


from ctypes import * pthread = cdll.LoadLibrary("libpthread-2.15.so") pthread.pthread_cancel(c_ulong(t.ident))

t es tu objeto de Thread .

Lee la fuente de python ( Modules/threadmodule.c y Python/thread_pthread.h ) puedes ver que Thread.ident es un tipo pthread_t , por lo que puedes hacer cualquier cosa que pthread pueda hacer en python con libpthread .