Ich bin sehr neu in Python und Multithread-Programmierung im Allgemeinen. Grundsätzlich habe ich ein Skript, das Dateien an einen anderen Speicherort kopiert. Ich möchte, dass dies in einem anderen Thread platziert wird, damit ich ausgeben kann, ....
um anzuzeigen, dass das Skript noch ausgeführt wird.
Das Problem, das ich habe, ist, dass wenn die Dateien nicht kopiert werden können, es eine Ausnahme auslöst. Dies ist in Ordnung, wenn es im Hauptthread ausgeführt wird. Der folgende Code funktioniert jedoch nicht:
try:
threadClass = TheThread(param1, param2, etc.)
threadClass.start() ##### **Exception takes place here**
except:
print "Caught an exception"
In der Thread-Klasse selbst habe ich versucht, die Ausnahme erneut auszulösen, aber es funktioniert nicht. Ich habe Leute hier gesehen, die ähnliche Fragen gestellt haben, aber sie scheinen alle etwas Spezifischeres zu tun als das, was ich versuche (und ich verstehe die angebotenen Lösungen nicht ganz). Ich habe Leute gesehen, die die Verwendung von erwähnt haben sys.exc_info()
, aber ich weiß nicht, wo oder wie ich sie verwenden soll.
Jede Hilfe wird sehr geschätzt!
EDIT: Der Code für die Thread-Klasse ist unten:
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
? Codebeispiel vielleicht?Antworten:
Das Problem ist, dass
thread_obj.start()
sofort zurückkehrt. Der untergeordnete Thread, den Sie erzeugt haben, wird in einem eigenen Kontext mit einem eigenen Stapel ausgeführt. Jede Ausnahme, die dort auftritt, befindet sich im Kontext des untergeordneten Threads und befindet sich in einem eigenen Stapel. Eine Möglichkeit, die ich mir derzeit vorstellen kann, um diese Informationen an den übergeordneten Thread zu übermitteln, ist die Verwendung einer Art Nachrichtenübermittlung.Probieren Sie dies für die Größe an:
quelle
multiprocessing
Äquivalent: gist.github.com/2311116bucket.get()
Auslösen eine Ausnahme auslöstQueue.Empty
? Dann wird der Threadjoin(0.1)
abgeschlossen undisAlive() is False
und Sie verpassen Ihre Ausnahme.Queue
ist in diesem einfachen Fall nicht erforderlich - Sie können die Ausnahmeinformationen einfach als Eigenschaft von speichern,ExcThread
solange Sie sicherstellen, dassrun()
sie direkt nach der Ausnahme abgeschlossen werden (was in diesem einfachen Beispiel der Fall ist). Dann lösen Sie die Ausnahme einfach nach (oder während) erneut aust.join()
. Es gibt keine Synchronisationsprobleme, dajoin()
sichergestellt wird, dass der Thread abgeschlossen ist. Siehe Antwort von Rok Strniša unter stackoverflow.com/a/12223550/126362Das
concurrent.futures
Modul macht es einfach, in separaten Threads (oder Prozessen) zu arbeiten und alle daraus resultierenden Ausnahmen zu behandeln:concurrent.futures
ist in Python 3.2 enthalten und als backportiertesfutures
Modul für frühere Versionen verfügbar .quelle
concurrent.futures.as_completed
können Sie sofort benachrichtigt werden, wenn Ausnahmen ausgelöstEs gibt viele wirklich seltsam komplizierte Antworten auf diese Frage. Vereinfache ich das zu stark, weil mir das für die meisten Dinge ausreicht?
Wenn Sie sicher sind, dass Sie immer nur auf der einen oder anderen Version von Python ausgeführt werden, können Sie die
run()
Methode auf die verstümmelte Version reduzieren (wenn Sie nur auf Versionen von Python vor 3 ausgeführt werden) oder Nur die saubere Version (wenn Sie nur mit Python-Versionen ab 3 arbeiten).Anwendungsbeispiel:
Und Sie werden die Ausnahme sehen, die auf dem anderen Thread ausgelöst wird, wenn Sie beitreten.
Wenn Sie
six
nur Python 3 verwenden oder verwenden, können Sie die Stack-Trace-Informationen verbessern, die Sie erhalten, wenn die Ausnahme erneut ausgelöst wird. Anstelle nur des Stapels am Punkt des Joins können Sie die innere Ausnahme in eine neue äußere Ausnahme einschließen und beide Stapelspuren mit abrufenoder
quelle
Obwohl es nicht möglich ist, eine in einem anderen Thread ausgelöste Ausnahme direkt abzufangen, finden Sie hier einen Code, mit dem Sie ganz transparent etwas erhalten können, das dieser Funktionalität sehr nahe kommt. Ihr untergeordneter Thread muss die
ExThread
Klasse anstelle vonthreading.Thread
klassifizieren und der übergeordnete Thread muss diechild_thread.join_with_exception()
Methode anstelle von aufrufen,child_thread.join()
wenn er darauf wartet, dass der Thread seinen Job beendet.Technische Details dieser Implementierung: Wenn der untergeordnete Thread eine Ausnahme auslöst, wird diese über a
Queue
an den übergeordneten Thread übergeben und im übergeordneten Thread erneut ausgelöst. Beachten Sie, dass bei diesem Ansatz kein Warten erforderlich ist.quelle
BaseException
, nichtException
? Alles, was Sie tun, ist, die Ausnahme von einerThread
zur anderen zu verbreiten. Im MomentKeyboardInterrupt
würde IE, a , stillschweigend ignoriert, wenn es in einem Hintergrund-Thread ausgelöst würde.join_with_exception
hängt auf unbestimmte Zeit, wenn ein zweites Mal an einem toten Thread aufgerufen wird. Fix: github.com/fraserharris/threading-extensions/blob/master/…Queue
es notwendig ist; Siehe meinen Kommentar zu @ Santas Antwort. Sie können es bis zu etwas wie Rok Strnišas Antwort unter stackoverflow.com/a/12223550/126362Wenn eine Ausnahme in einem Thread auftritt, ist es am besten, sie währenddessen während des Aufruferthreads erneut auszulösen
join
. Mit dersys.exc_info()
Funktion können Sie Informationen zu der Ausnahme erhalten, die derzeit behandelt wird . Diese Informationen können einfach als Eigenschaft des Thread-Objekts gespeichert werden, bis siejoin
aufgerufen werden. An diesem Punkt können sie erneut ausgelöst werden.Beachten Sie, dass a
Queue.Queue
(wie in anderen Antworten vorgeschlagen) in diesem einfachen Fall nicht erforderlich ist, in dem der Thread höchstens 1 Ausnahme auslöst und direkt nach dem Auslösen einer Ausnahme abgeschlossen wird . Wir vermeiden Rennbedingungen, indem wir einfach darauf warten, dass der Thread abgeschlossen ist.Erweitern Sie beispielsweise
ExcThread
(unten) und überschreiben SieexcRun
(anstelle vonrun
).Python 2.x:
Python 3.x:
Das 3-Argument-Formular für
raise
ist in Python 3 nicht mehr vorhanden. Ändern Sie daher die letzte Zeile in:quelle
concurrent.futures.as_completed
https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed
Die folgende Lösung:
Queue
Quelle:
Mögliche Ausgabe:
Es ist leider nicht möglich, Futures zu töten, um die anderen abzubrechen, wenn einer fehlschlägt:
concurrent.features
;; Python: concurrent.futures Wie kann ich es stornierbar machen?threading
: Gibt es eine Möglichkeit, einen Thread zu töten?Wenn Sie etwas tun wie:
dann
with
fängt der es und wartet, bis der zweite Faden fertig ist, bevor er fortfährt. Folgendes verhält sich ähnlich:da
future.result()
löst die Ausnahme erneut aus, wenn eine aufgetreten ist.Wenn Sie den gesamten Python-Prozess beenden möchten, kommen Sie möglicherweise davon
os._exit(0)
, aber dies bedeutet wahrscheinlich, dass Sie einen Refactor benötigen.Benutzerdefinierte Klasse mit perfekter Ausnahmesemantik
Am Ende habe ich die perfekte Oberfläche für mich selbst programmiert: Der richtige Weg, um die maximale Anzahl von Threads zu begrenzen, die gleichzeitig ausgeführt werden? Abschnitt "Warteschlangenbeispiel mit Fehlerbehandlung". Diese Klasse soll sowohl praktisch sein als auch Ihnen die vollständige Kontrolle über die Übermittlung und die Behandlung von Ergebnissen / Fehlern geben.
Getestet unter Python 3.6.7, Ubuntu 18.04.
quelle
Dies war ein böses kleines Problem, und ich würde gerne meine Lösung einbringen. Einige andere Lösungen, die ich gefunden habe (z. B. async.io), sahen vielversprechend aus, präsentierten aber auch eine Art Black Box. Der Ansatz der Warteschlangen- / Ereignisschleife bindet Sie an eine bestimmte Implementierung. Der Quellcode für gleichzeitige Futures umfasst jedoch nur etwa 1000 Zeilen und ist leicht zu verstehen . Dadurch konnte ich mein Problem leicht lösen: Ad-hoc-Arbeitsthreads ohne viel Setup erstellen und Ausnahmen im Hauptthread abfangen.
Meine Lösung verwendet die Concurrent Futures API und die Threading API. Sie können einen Worker erstellen, der Ihnen sowohl den Thread als auch die Zukunft bietet. Auf diese Weise können Sie dem Thread beitreten, um auf das Ergebnis zu warten:
... oder Sie können den Mitarbeiter einfach einen Rückruf senden lassen, wenn Sie fertig sind:
... oder Sie können eine Schleife ausführen, bis das Ereignis abgeschlossen ist:
Hier ist der Code:
... und die Testfunktion:
quelle
Als Neuling im Threading habe ich lange gebraucht, um zu verstehen, wie man den Code von Mateusz Kobos (oben) implementiert. Hier ist eine geklärte Version, um zu verstehen, wie man sie benutzt.
quelle
Ähnlich wie bei RickardSjogren ohne Queue, System usw., aber auch ohne einige Listener für Signale: Führen Sie direkt einen Ausnahmebehandler aus, der einem Ausnahmeblock entspricht.
Nur self._callback und der Ausnahmeblock in run () sind zusätzlich zum normalen Threading.Thread.
quelle
Ich weiß, dass ich hier etwas zu spät zur Party komme, aber ich hatte ein sehr ähnliches Problem, aber es beinhaltete die Verwendung von tkinter als GUI, und der Mainloop machte es unmöglich, eine der Lösungen zu verwenden, die von .join () abhängen. Daher habe ich die im EDIT der ursprünglichen Frage angegebene Lösung angepasst, sie jedoch allgemeiner gestaltet, um das Verständnis für andere zu erleichtern.
Hier ist die neue Thread-Klasse in Aktion:
Natürlich können Sie die Ausnahme jederzeit auf eine andere Weise als durch Protokollieren behandeln lassen, z. B. durch Ausdrucken oder Ausgeben an die Konsole.
Auf diese Weise können Sie die ExceptionThread-Klasse genau wie die Thread-Klasse ohne besondere Änderungen verwenden.
quelle
Eine Methode, die ich mag, basiert auf dem Beobachtermuster . Ich definiere eine Signalklasse, die mein Thread verwendet, um Ausnahmen an Listener zu senden. Es kann auch verwendet werden, um Werte von Threads zurückzugeben. Beispiel:
Ich habe nicht genug Erfahrung mit Threads, um zu behaupten, dass dies eine völlig sichere Methode ist. Aber es hat bei mir funktioniert und ich mag die Flexibilität.
quelle
Nackte Ausnahmen zu verwenden ist keine gute Praxis, da Sie normalerweise mehr fangen, als Sie erwarten.
Ich würde vorschlagen, das
except
zu ändern, um NUR die Ausnahme zu erfassen, die Sie behandeln möchten. Ich denke nicht, dass das Erhöhen den gewünschten Effekt hat, denn wenn SieTheThread
im Äußeren instanziieren undtry
eine Ausnahme auslösen, wird die Zuweisung niemals stattfinden.Stattdessen möchten Sie möglicherweise nur darauf aufmerksam machen und fortfahren, z. B.:
Wenn diese Ausnahme abgefangen wird, können Sie sie dort behandeln. Wenn das Äußere
try
eine Ausnahme abfängtTheThread
, wissen Sie, dass es nicht die ist, die Sie bereits behandelt haben, und helfen Ihnen dabei, Ihren Prozessablauf zu isolieren.quelle
Eine einfache Möglichkeit, die Ausnahme des Threads abzufangen und mit der Aufrufermethode zu kommunizieren, besteht darin, ein Wörterbuch oder eine Liste an die
worker
Methode zu übergeben.Beispiel (Übergabe des Wörterbuchs an die Worker-Methode):
quelle
Wrap Thread mit Ausnahmespeicher.
quelle
pygolang stellt sync.WorkGroup bereit, die insbesondere Ausnahmen von erzeugten Arbeitsthreads an den Hauptthread weitergibt . Beispielsweise:
gibt beim Ausführen Folgendes an:
Der ursprüngliche Code aus der Frage wäre nur:
quelle