Soy muy nuevo en Python y en la programación multiproceso en general. Básicamente, tengo un script que copiará archivos a otra ubicación. Me gustaría que esto se coloque en otro hilo para que pueda mostrar ....
que el script todavía se está ejecutando.
El problema que tengo es que si los archivos no se pueden copiar, arrojará una excepción. Esto está bien si se ejecuta en el hilo principal; sin embargo, tener el siguiente código no funciona:
try:
threadClass = TheThread(param1, param2, etc.)
threadClass.start() ##### **Exception takes place here**
except:
print "Caught an exception"
En la clase de hilo en sí, intenté volver a lanzar la excepción, pero no funciona. He visto a personas aquí hacer preguntas similares, pero todos parecen estar haciendo algo más específico de lo que estoy tratando de hacer (y no entiendo muy bien las soluciones ofrecidas). He visto a personas mencionar el uso de sys.exc_info()
, sin embargo, no sé dónde ni cómo usarlo.
¡Toda ayuda es muy apreciada!
EDITAR: El código para la clase de subproceso está a continuación:
class TheThread(threading.Thread):
def __init__(self, sourceFolder, destFolder):
threading.Thread.__init__(self)
self.sourceFolder = sourceFolder
self.destFolder = destFolder
def run(self):
try:
shul.copytree(self.sourceFolder, self.destFolder)
except:
raise
TheThread
? ¿Ejemplo de código tal vez?Respuestas:
El problema es ese
thread_obj.start()
vuelve de inmediato. El subproceso hijo que generó se ejecuta en su propio contexto, con su propia pila. Cualquier excepción que ocurra allí está en el contexto del subproceso secundario y está en su propia pila. Una forma en la que puedo pensar en este momento para comunicar esta información al hilo primario es mediante el uso de algún tipo de paso de mensajes, por lo que podría investigar eso.Probar esto para el tamaño:
fuente
multiprocessing
equivalente: gist.github.com/2311116bucket.get()
aumentosQueue.Empty
? Entonces el hilojoin(0.1)
se completará yisAlive() is False
, y perderás tu excepción.Queue
es innecesario en este caso simple: solo puede almacenar la información de excepción como una propiedad de la mismaExcThread
, siempre y cuando se asegure de que serun()
complete justo después de la excepción (lo que hace en este ejemplo simple). Luego, simplemente vuelve a generar la excepción después (o durante)t.join()
. No hay problemas de sincronización porquejoin()
se asegura de que el hilo se haya completado. Vea la respuesta de Rok Strniša a continuación stackoverflow.com/a/12223550/126362El
concurrent.futures
módulo simplifica el trabajo en subprocesos (o procesos) separados y maneja las excepciones resultantes:concurrent.futures
se incluye con Python 3.2, y está disponible como módulo con respaldofutures
para versiones anteriores.fuente
concurrent.futures.as_completed
, puede recibir una notificación inmediata a medida que seHay muchas respuestas realmente extrañas a esta pregunta. ¿Estoy simplificando demasiado esto, porque esto me parece suficiente para la mayoría de las cosas?
Si está seguro de que solo se ejecutará en una u otra versión de Python, puede reducir el
run()
método a la versión destrozada (si solo se ejecutará en versiones de Python anteriores a la 3), o solo la versión limpia (si solo se ejecutará en versiones de Python que comiencen con 3).Ejemplo de uso:
Y verá la excepción planteada en el otro hilo cuando se una.
Si está utilizando
six
o solo en Python 3, puede mejorar la información de seguimiento de la pila que obtiene cuando se vuelve a generar la excepción. En lugar de solo la pila en el punto de la unión, puede ajustar la excepción interna en una nueva excepción externa y obtener ambas trazas de pila cono
fuente
Aunque no es posible detectar directamente una excepción lanzada en un hilo diferente, aquí hay un código para obtener de manera bastante transparente algo muy cercano a esta funcionalidad. Su subproceso secundario debe subclasificar la
ExThread
clase en lugar dethreading.Thread
y el subproceso primario debe llamar alchild_thread.join_with_exception()
método en lugar dechild_thread.join()
cuando espera que el subproceso termine su trabajo.Detalles técnicos de esta implementación: cuando el subproceso secundario genera una excepción, se pasa al primario a través de
Queue
ay se vuelve a generar en el subproceso principal. Tenga en cuenta que no hay ocupados esperando en este enfoque.fuente
BaseException
, noException
? Todo lo que estás haciendo es propagar la excepción de unoThread
a otro. En este momento, IE, aKeyboardInterrupt
, se ignoraría en silencio si se generara en un hilo de fondo.join_with_exception
se cuelga indefinidamente si se llama por segunda vez en un hilo muerto. Solución: github.com/fraserharris/threading-extensions/blob/master/…Queue
sea necesario; mira mi comentario a la respuesta de @ Santa. Puede simplificarlo a algo como la respuesta de Rok Strniša a continuación stackoverflow.com/a/12223550/126362Si ocurre una excepción en un hilo, la mejor manera es volver a subirlo en el hilo del llamante durante
join
. Puede obtener información sobre la excepción que se está manejando actualmente utilizando lasys.exc_info()
función. Esta información simplemente puede almacenarse como una propiedad del objeto de subproceso hastajoin
se llame, momento en el que se puede volver a generar.Tenga en cuenta que a
Queue.Queue
(como se sugiere en otras respuestas) no es necesario en este caso simple donde el hilo arroja como máximo 1 excepción y se completa justo después de arrojar una excepción . Evitamos las condiciones de carrera simplemente esperando que se complete el hilo.Por ejemplo, extienda
ExcThread
(abajo), anulandoexcRun
(en lugar derun
).Python 2.x:
Python 3.x:
El formulario de 3 argumentos para
raise
desapareció en Python 3, así que cambie la última línea a:fuente
concurrent.futures.as_completed
https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed
La siguiente solución:
Queue
Fuente:
Salida posible:
Desafortunadamente, no es posible matar futuros para cancelar los otros ya que uno falla:
concurrent.features
; Python: concurrent.futures ¿Cómo hacer que sea cancelable?threading
: ¿Hay alguna forma de matar un hilo?Si haces algo como:
luego lo
with
atrapa y espera a que termine el segundo hilo antes de continuar. Lo siguiente se comporta de manera similar:ya que
future.result()
vuelve a plantear la excepción si se produjo una.Si desea salir de todo el proceso de Python, puede salirse con la suya
os._exit(0)
, pero esto probablemente significa que necesita un refactorizador.Clase personalizada con semántica de excepción perfecta
Terminé codificando la interfaz perfecta para mí en: ¿ La forma correcta de limitar el número máximo de hilos que se ejecutan a la vez? sección "Ejemplo de cola con manejo de errores". Esa clase pretende ser conveniente y brindarle un control total sobre el envío y el manejo de resultados / errores.
Probado en Python 3.6.7, Ubuntu 18.04.
fuente
Este fue un pequeño problema desagradable, y me gustaría incluir mi solución. Algunas otras soluciones que encontré (por ejemplo, async.io) parecían prometedoras pero también presentaban un cuadro negro. El enfoque de bucle de cola / evento lo vincula a una determinada implementación. Sin embargo, el código fuente de futuros concurrentes es de alrededor de 1000 líneas y es fácil de comprender. . Me permitió resolver fácilmente mi problema: crear hilos de trabajo ad-hoc sin mucha configuración, y poder detectar excepciones en el hilo principal.
Mi solución utiliza la API de futuros concurrentes y la API de subprocesos. Le permite crear un trabajador que le proporciona tanto el hilo como el futuro. De esa manera, puedes unirte al hilo para esperar el resultado:
... o puede dejar que el trabajador solo envíe una devolución de llamada cuando haya terminado:
... o puedes recorrer hasta que se complete el evento:
Aquí está el código:
... y la función de prueba:
fuente
Como novato en Threading, me llevó mucho tiempo entender cómo implementar el código de Mateusz Kobos (arriba). Aquí hay una versión aclarada para ayudar a entender cómo usarla.
fuente
De manera similar a RickardSjogren's sin Queue, sys, etc. pero también sin algunos oyentes a las señales: ejecute directamente un controlador de excepción que corresponda a un bloque except.
Solo self._callback y el bloque except en run () es adicional al subproceso normal.
fuente
Sé que llegué un poco tarde a la fiesta aquí, pero estaba teniendo un problema muy similar, pero incluía el uso de tkinter como GUI, y el mainloop hizo imposible usar cualquiera de las soluciones que dependen de .join (). Por lo tanto, adapté la solución dada en el EDIT de la pregunta original, pero la hice más general para que sea más fácil de entender para los demás.
Aquí está la nueva clase de hilo en acción:
Por supuesto, siempre puede hacer que maneje la excepción de alguna otra manera desde el inicio de sesión, como imprimirlo o hacer que se envíe a la consola.
Esto le permite usar la clase ExceptionThread exactamente como lo haría con la clase Thread, sin ninguna modificación especial.
fuente
Un método que me gusta se basa en el patrón de observación . Defino una clase de señal que mi hilo utiliza para emitir excepciones a los oyentes. También se puede usar para devolver valores de subprocesos. Ejemplo:
No tengo suficiente experiencia trabajando con hilos para afirmar que este es un método completamente seguro. Pero me ha funcionado y me gusta la flexibilidad.
fuente
El uso de excepciones desnudas no es una buena práctica, ya que generalmente captura más de lo que negocia.
Sugeriría modificar el
except
para capturar SOLO la excepción que le gustaría manejar. No creo que elevarlo tenga el efecto deseado, porque cuando vas a crear una instanciaTheThread
en el exteriortry
, si genera una excepción, la asignación nunca va a suceder.En su lugar, es posible que desee alertarlo y seguir adelante, como:
Luego, cuando se detecta esa excepción, puede manejarla allí. Luego, cuando el exterior
try
detecta una excepciónTheThread
, sabe que no será el que ya manejó y lo ayudará a aislar el flujo de su proceso.fuente
Una forma sencilla de detectar la excepción del hilo y comunicarse con el método de la persona que llama podría ser pasar un diccionario o una lista a
worker
método.Ejemplo (pasar del diccionario al método de trabajo):
fuente
Hilo de envoltura con almacenamiento de excepción.
fuente
pygolang proporciona sync.WorkGroup que, en particular, propaga la excepción de los hilos de trabajo generados al hilo principal. Por ejemplo:
da lo siguiente cuando se ejecuta:
El código original de la pregunta sería simplemente:
fuente