248 votes

Catch une exception de fil dans le thread appelant en Python

Je suis très nouveau pour Python et la programmation multithread en général. En gros, j'ai un script qui permet de copier des fichiers vers un autre emplacement. Je voudrais être placé dans un autre thread, donc je peux sortie .... pour indiquer que le script est encore en cours d'exécution.

Le problème que je rencontre est que si les fichiers ne peuvent pas être copiés, il va lever une exception. C'est ok si en cours d'exécution dans le thread principal; toutefois, ayant le code suivant ne fonctionne pas:

try:
    threadClass = TheThread(param1, param2, etc.)
    threadClass.start()   ##### **Exception takes place here**
except:
    print "Caught an exception"

Dans la classe thread lui-même, j'ai essayé de re-lancer l'exception, mais il ne fonctionne pas. J'ai vu des gens ici poser des questions similaires, mais ils semblent tous être en train de faire quelque chose de plus précis que ce que je suis en train de faire (et je ne suis pas tout à fait comprendre les solutions proposées). J'ai vu des gens parler de l'utilisation de sys.exc_info(), cependant je ne sais pas où ou comment l'utiliser.

Toute aide est grandement appréciée!

EDIT: le code de La classe thread ci-dessous:

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

179voto

Lasse V. Karlsen Points 148037

Vous devez penser à des threads en termes d'appels téléphoniques.

Considérez ceci.

Vous appelez la mairie et poser une question. Alors qu'ils trouvent la réponse pour vous, vous le tenez. Quand ils ont la réponse, ils vont le dire à vous, et vous raccrochez. Si, pour une raison quelconque, ils ne peuvent pas trouver la réponse (exception), ils vous diront que.

C'est de cette manière synchrone, normal, appel de méthode fonctionne. Vous appelez une méthode, quand il revient, vous avez la réponse (bonne ou mauvaise.)

Cependant, un fil va plus comme ceci:

Vous appelez de la mairie et de poser une question, et demandez-leur de vous appeler en arrière quand ils ont la réponse. Vous raccrocher.

À ce stade, vous ne savez pas s'ils vont trouver la réponse ou pas, de sorte que toute tentative, maintenant, pour essayer de gérer le résultat de l'enquête, ne va pas, que vous n'avez simplement pas les résultats encore.

Au lieu de cela, vous devez réagir à l'appel entrant, et de prendre les nouvelles, bonnes ou mauvaises, et les traiter ensuite.

En termes de votre code, vous devez disposez d'un code qui réagit à votre fil à défaut, et les journaux ou les processus de l'exception. Le code que vous avez dans votre question, vous dites ne fonctionne pas, c'est comme essayer de traiter les résultats de l'appel téléphonique à droite après avoir raccroché, lorsque vous n'avez pas encore la réponse.

127voto

Santa Points 6013

Le problème est que thread_obj.start() revient immédiatement. Le thread enfant que vous avez créé s'exécute dans son propre contexte, avec sa propre pile. Toute exception qui s'y produit se trouve dans le contexte du thread enfant et se trouve dans sa propre pile. Une façon dont je peux penser en ce moment pour communiquer ces informations au thread parent consiste à utiliser une sorte de message de transmission, afin que vous puissiez examiner cela.

Essayez ceci pour la taille:

 import sys
import threading
import Queue


class ExcThread(threading.Thread):

    def __init__(self, bucket):
        threading.Thread.__init__(self)
        self.bucket = bucket

    def run(self):
        try:
            raise Exception('An error occured here.')
        except Exception:
            self.bucket.put(sys.exc_info())


def main():
    bucket = Queue.Queue()
    thread_obj = ExcThread(bucket)
    thread_obj.start()

    while True:
        try:
            exc = bucket.get(block=False)
        except Queue.Empty:
            pass
        else:
            exc_type, exc_obj, exc_trace = exc
            # deal with the exception
            print exc_type, exc_obj
            print exc_trace

        thread_obj.join(0.1)
        if thread_obj.isAlive():
            continue
        else:
            break


if __name__ == '__main__':
    main()
 

55voto

Jon-Eric Points 7749

L' concurrent.futures module facilite le travail dans des threads séparés (ou processus) et gérer les exceptions résultant:

import concurrent.futures
import shutil

def copytree_with_dots(src_path, dst_path):
    with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
        # Execute the copy on a separate thread,
        # creating a future object to track progress.
        future = executor.submit(shutil.copytree, src_path, dst_path)

        while future.running():
            # Print pretty dots here.
            pass

        # Return the value returned by shutil.copytree(), None.
        # Raise any exceptions raised during the copy process.
        return future.result()

concurrent.futures est inclus avec Python 3.2, et est disponible comme le rétroportage futures module pour les versions antérieures.

31voto

Mateusz Kobos Points 81

Bien qu'il n'est pas possible d'intercepter une exception levée dans un thread différent, voici un code de manière assez transparente obtenir quelque chose de très proche de cette fonctionnalité. Votre enfant thread doit sous-classe de l' ExThread classe au lieu d' threading.Thread et le parent thread doit appeler l' child_thread.join_with_exception() méthode de child_thread.join() lors de l'attente pour le fil à terminer son travail.

Détails techniques de cette mise en œuvre: lorsque l'enfant fil déclenche une exception, il est transmis à la société mère par le biais d'un Queue et jeté de nouveau dans le parent de fil. Notez qu'il n'y a pas occupé d'attente dans cette approche .

#!/usr/bin/env python

import sys
import threading
import Queue

class ExThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.__status_queue = Queue.Queue()

    def run_with_exception(self):
        """This method should be overriden."""
        raise NotImplementedError

    def run(self):
        """This method should NOT be overriden."""
        try:
            self.run_with_exception()
        except Exception:
            self.__status_queue.put(sys.exc_info())
        self.__status_queue.put(None)

    def wait_for_exc_info(self):
        return self.__status_queue.get()

    def join_with_exception(self):
        ex_info = self.wait_for_exc_info()
        if ex_info is None:
            return
        else:
            raise ex_info[1]

class MyException(Exception):
    pass

class MyThread(ExThread):
    def __init__(self):
        ExThread.__init__(self)

    def run_with_exception(self):
        thread_name = threading.current_thread().name
        raise MyException("An error in thread '{}'.".format(thread_name))

def main():
    t = MyThread()
    t.start()
    try:
        t.join_with_exception()
    except MyException as ex:
        thread_name = threading.current_thread().name
        print "Caught a MyException in thread '{}': {}".format(thread_name, ex)

if __name__ == '__main__':
    main()

23voto

Darthenius Points 1828

Si une exception se produit dans un thread, le meilleur moyen consiste à le relancer dans le thread de l'appelant au cours de join . Étendre ExcThread (en dessous), en remplaçant excRun (au lieu de run ).

 import threading

class ExcThread(threading.Thread):
  def excRun(self):
    pass

  def run(self):
    self.exc = None
    try:
      self.excRun()
    except:
      import sys
      self.exc = sys.exc_info()

  def join(self):
    threading.Thread.join(self)
    if self.exc:
      msg = "Thread '%s' threw an exception: %s" % (self.getName(), self.exc[1])
      new_exc = Exception(msg)
      raise new_exc.__class__, new_exc, self.exc[2]
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X