Je suis très nouveau en Python et en programmation multithread en général. Fondamentalement, j'ai un script qui copiera les fichiers vers un autre emplacement. Je voudrais que cela soit placé dans un autre thread afin que je puisse sortir ....
pour indiquer que le script est toujours en cours d'exécution.
Le problème que j'ai, c'est que si les fichiers ne peuvent pas être copiés, il lèvera une exception. C'est correct si vous utilisez le thread principal; cependant, avoir 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 de thread elle-même, j'ai essayé de relancer l'exception, mais cela ne fonctionne pas. J'ai vu des gens ici poser des questions similaires, mais ils semblent tous faire quelque chose de plus spécifique que ce que j'essaie de faire (et je ne comprends pas très bien les solutions proposées). J'ai vu des gens mentionner l'utilisation de sys.exc_info()
, mais je ne sais pas où ni comment l'utiliser.
Toute aide est grandement appréciée!
EDIT: Le code de la classe de thread est 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
TheThread
? Exemple de code peut-être?Réponses:
Le problème est qu'il
thread_obj.start()
revient immédiatement. Le thread enfant que vous avez généré 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 est en utilisant une sorte de message qui passe, donc vous pourriez examiner cela.Essayez ceci pour la taille:
la source
multiprocessing
équivalent: gist.github.com/2311116bucket.get()
relanceQueue.Empty
? Ensuite, le filjoin(0.1)
se terminera etisAlive() is False
, et vous manquerez votre exception.Queue
est inutile dans ce cas simple - vous pouvez simplement stocker les informations d'exception en tant que propriété deExcThread
tant que vous vous assurez que cela serun()
termine juste après l'exception (ce qu'il fait dans cet exemple simple). Ensuite, vous relancez simplement l'exception après (ou pendant)t.join()
. Il n'y a aucun problème de synchronisation car iljoin()
s'assure que le thread est terminé. Voir la réponse de Rok Strniša ci-dessous stackoverflow.com/a/12223550/126362Le
concurrent.futures
module simplifie le travail dans des threads (ou processus) séparés et gère toutes les exceptions résultantes:concurrent.futures
est inclus avec Python 3.2 et est disponible en tant que module rétroportéfutures
pour les versions antérieures.la source
concurrent.futures.as_completed
, vous pouvez être immédiatement averti lorsque des exceptions sont levées: stackoverflow.com/questions/2829329/…Il y a beaucoup de réponses vraiment étrangement compliquées à cette question. Suis-je trop simplifier cela, car cela me semble suffisant pour la plupart des choses.
Si vous êtes certain que vous n'exécuterez jamais que sur l'une ou l'autre version de Python, vous pouvez réduire la
run()
méthode à la seule version modifiée (si vous n'exécutez que sur des versions de Python avant 3), ou juste la version propre (si vous n'exécutez que sur des versions de Python commençant par 3).Exemple d'utilisation:
Et vous verrez l'exception levée sur l'autre thread lorsque vous vous joindrez.
Si vous utilisez
six
ou sur Python 3 uniquement, vous pouvez améliorer les informations de trace de pile que vous obtenez lorsque l'exception est levée à nouveau. Au lieu de seulement la pile au point de la jointure, vous pouvez encapsuler l'exception interne dans une nouvelle exception externe et obtenir les deux traces de pile avecou
la source
Bien qu'il ne soit pas possible d'attraper directement une exception levée dans un thread différent, voici un code pour obtenir de manière très transparente quelque chose de très proche de cette fonctionnalité. Votre thread enfant doit sous-
ExThread
classer la classe au lieu dethreading.Thread
et le thread parent doit appeler lachild_thread.join_with_exception()
méthode au lieu dechild_thread.join()
lorsqu'il attend que le thread termine son travail.Détails techniques de cette implémentation: lorsque le thread enfant lève une exception, elle est transmise au parent via a
Queue
et renvoyée dans le thread parent. Notez qu'il n'y a pas d'attente occupée dans cette approche.la source
BaseException
, nonException
? Tout ce que vous faites, c'est propager l'exception de l'unThread
vers l'autre. À l'heure actuelle, IE, aKeyboardInterrupt
, serait silencieusement ignoré s'il était levé dans un thread d'arrière-plan.join_with_exception
se bloque indéfiniment s'il est appelé une deuxième fois sur un thread mort. Correctif: github.com/fraserharris/threading-extensions/blob/master/…Queue
soit nécessaire; voir mon commentaire sur la réponse de @ Santa. Vous pouvez le simplifier jusqu'à quelque chose comme la réponse de Rok Strniša ci-dessous stackoverflow.com/a/12223550/126362Si une exception se produit dans un thread, la meilleure façon est de la relancer dans le thread appelant pendant
join
. Vous pouvez obtenir des informations sur l'exception en cours de traitement à l'aide de lasys.exc_info()
fonction. Ces informations peuvent simplement être stockées en tant que propriété de l'objet thread jusqu'àjoin
ce qu'elles soient appelées, moment auquel elles peuvent être remontées.Notez qu'un
Queue.Queue
(comme suggéré dans d'autres réponses) n'est pas nécessaire dans ce cas simple où le thread lève au plus 1 exception et se termine juste après avoir levé une exception . Nous évitons les conditions de concurrence en attendant simplement la fin du thread.Par exemple, étendez
ExcThread
(ci-dessous), en remplaçantexcRun
(au lieu derun
).Python 2.x:
Python 3.x:
La forme à 3 arguments pour
raise
est partie en Python 3, changez donc la dernière ligne en:la source
concurrent.futures.as_completed
https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed
La solution suivante:
Queue
La source:
Sortie possible:
Il n'est malheureusement pas possible de tuer des futures pour annuler les autres car on échoue:
concurrent.features
; Python: concurrent.futures Comment le rendre annulable?threading
: Existe - t-il un moyen de tuer un fil?Si vous faites quelque chose comme:
puis l'
with
attrape et attend la fin du deuxième thread avant de continuer. Le comportement suivant est similaire:depuis
future.result()
relance l'exception si elle se produit.Si vous souhaitez quitter l'intégralité du processus Python, vous pouvez vous en sortir
os._exit(0)
, mais cela signifie probablement que vous avez besoin d'un refactor.Classe personnalisée avec une sémantique d'exception parfaite
J'ai fini par coder pour moi-même l'interface parfaite: la bonne façon de limiter le nombre maximal de threads s'exécutant simultanément? section "Exemple de file d'attente avec gestion des erreurs". Cette classe vise à être à la fois pratique et vous donne un contrôle total sur la soumission et la gestion des résultats / erreurs.
Testé sur Python 3.6.7, Ubuntu 18.04.
la source
C'était un petit problème désagréable, et j'aimerais jeter ma solution. Certaines autres solutions que j'ai trouvées (async.io par exemple) semblaient prometteuses mais présentaient aussi un peu une boîte noire. L'approche de la file d'attente / boucle d'événements vous relie en quelque sorte à une certaine implémentation. Le code source de contrats à terme simultanés, cependant, est d'environ 1000 lignes seulement, et facile à comprendre . Cela m'a permis de résoudre facilement mon problème: créer des threads de travail ad hoc sans trop de configuration et pouvoir intercepter des exceptions dans le thread principal.
Ma solution utilise l'API à terme simultanée et l'API de threading. Il vous permet de créer un travailleur qui vous donne à la fois le fil et l'avenir. De cette façon, vous pouvez rejoindre le thread pour attendre le résultat:
... ou vous pouvez laisser le travailleur simplement envoyer un rappel une fois terminé:
... ou vous pouvez boucler jusqu'à la fin de l'événement:
Voici le code:
... et la fonction de test:
la source
En tant que noobie de Threading, il m'a fallu beaucoup de temps pour comprendre comment implémenter le code de Mateusz Kobos (ci-dessus). Voici une version clarifiée pour vous aider à comprendre comment l'utiliser.
la source
De manière similaire à celle de RickardSjogren sans Queue, sys etc. mais aussi sans certains écouteurs de signaux: exécutez directement un gestionnaire d'exceptions qui correspond à un bloc except.
Seuls self._callback et le bloc except dans run () s'ajoutent au threading normal.
la source
Je sais que je suis un peu en retard pour la fête ici, mais je rencontrais un problème très similaire, mais cela incluait l'utilisation de tkinter comme interface graphique, et la boucle principale a rendu impossible l'utilisation des solutions qui dépendent de .join (). J'ai donc adapté la solution donnée dans l'EDIT de la question d'origine, mais je l'ai rendue plus générale pour la rendre plus facile à comprendre pour les autres.
Voici la nouvelle classe de threads en action:
Bien sûr, vous pouvez toujours lui faire gérer l'exception d'une autre manière de la journalisation, comme l'impression ou la sortie sur la console.
Cela vous permet d'utiliser la classe ExceptionThread exactement comme vous le feriez avec la classe Thread, sans aucune modification spéciale.
la source
Une méthode que j'aime est basée sur le modèle d'observateur . Je définis une classe de signal que mon thread utilise pour émettre des exceptions aux écouteurs. Il peut également être utilisé pour renvoyer des valeurs à partir de threads. Exemple:
Je n'ai pas assez d'expérience de travail avec les threads pour prétendre qu'il s'agit d'une méthode totalement sûre. Mais cela a fonctionné pour moi et j'aime la flexibilité.
la source
L'utilisation d'exceptions nues n'est pas une bonne pratique car vous attrapez généralement plus que ce que vous attendez.
Je suggérerais de modifier le
except
pour intercepter UNIQUEMENT l'exception que vous souhaitez gérer. Je ne pense pas que l'élévation ait l'effet souhaité, car lorsque vous allez instancierTheThread
à l'extérieurtry
, si elle déclenche une exception, l'affectation ne se produira jamais.Au lieu de cela, vous voudrez peut-être simplement l'alerter et continuer, comme:
Ensuite, lorsque cette exception est interceptée, vous pouvez la gérer à cet endroit. Ensuite, lorsque l'extérieur
try
détecte une exceptionTheThread
, vous savez que ce ne sera pas celui que vous avez déjà géré et vous aidera à isoler votre flux de processus.la source
Un moyen simple d'attraper l'exception de thread et de communiquer à nouveau avec la méthode de l'appelant pourrait être en passant le dictionnaire ou une liste à la
worker
méthode.Exemple (passage du dictionnaire à la méthode de travail):
la source
Envelopper le fil avec un stockage d'exception.
la source
pygolang fournit sync.WorkGroup qui, en particulier, propage l'exception des threads de travail générés au thread principal. Par exemple:
donne ce qui suit lors de l'exécution:
Le code d'origine de la question serait simplement:
la source