Jestem bardzo nowy w Pythonie i ogólnie programowaniu wielowątkowym. Zasadniczo mam skrypt, który skopiuje pliki do innej lokalizacji. Chciałbym, aby został on umieszczony w innym wątku, dzięki czemu mogę wysyłać dane ....
wskazujące, że skrypt nadal działa.
Problem, który mam, polega na tym, że jeśli nie można skopiować plików, wygeneruje wyjątek. Jest to OK, jeśli działa w głównym wątku; jednak posiadanie następującego kodu nie działa:
try:
threadClass = TheThread(param1, param2, etc.)
threadClass.start() ##### **Exception takes place here**
except:
print "Caught an exception"
W samej klasie wątków próbowałem ponownie zgłosić wyjątek, ale to nie działa. Widziałem ludzi tutaj zadających podobne pytania, ale wszyscy wydają się robić coś bardziej konkretnego niż to, co próbuję zrobić (i nie do końca rozumiem oferowane rozwiązania). Widziałem ludzi wspominających o użyciu sys.exc_info()
, jednak nie wiem, gdzie i jak go używać.
Każda pomoc jest mile widziana!
EDYCJA: Kod dla klasy wątku znajduje się poniżej:
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
? Może próbka kodu?Odpowiedzi:
Problem polega na tym, że
thread_obj.start()
natychmiast wraca. Wątek potomny, który odrodziłeś, wykonuje we własnym kontekście, z własnym stosem. Każdy występujący wyjątek występuje w kontekście wątku podrzędnego i znajduje się na swoim stosie. Jednym ze sposobów, które mogę teraz wymyślić, aby przekazać te informacje do wątku nadrzędnego, jest użycie pewnego rodzaju przekazywania wiadomości, abyś mógł to sprawdzić.Spróbuj tego dla rozmiaru:
źródło
multiprocessing
odpowiednik: gist.github.com/2311116bucket.get()
przebiciuQueue.Empty
? Wątekjoin(0.1)
zostanie zakończonyisAlive() is False
, a ty przegapisz swój wyjątek.Queue
nie jest konieczne w tym prostym przypadku - możesz po prostu przechowywać informacje o wyjątku jako właściwość, oExcThread
ile upewniasz się, żerun()
wypełnia się ono zaraz po wyjątku (co robi w tym prostym przykładzie). Następnie po prostu ponownie podnosisz wyjątek po (lub podczas)t.join()
. Nie ma problemów z synchronizacją, ponieważjoin()
upewnia się, że wątek został zakończony. Zobacz odpowiedź Rok Strniša poniżej stackoverflow.com/a/12223550/126362concurrent.futures
Moduł ułatwia działają w odrębnych wątków (lub procesów) i obsługiwać wszelkie wynikające wyjątki:concurrent.futures
jest dołączony Pythonie 3.2 i jest dostępny na przeniesionafutures
modułu do wcześniejszych wersji.źródło
concurrent.futures.as_completed
można uzyskać natychmiast powiadomiony wyjątki są podniesione: stackoverflow.com/questions/2829329/...Istnieje wiele naprawdę dziwnie skomplikowanych odpowiedzi na to pytanie. Upraszczam to, ponieważ wydaje mi się to wystarczające dla większości rzeczy.
Jeśli masz pewność, że kiedykolwiek będziesz działał tylko na jednej lub drugiej wersji Pythona, możesz zredukować
run()
metodę do tylko zniekształconej wersji (jeśli będziesz działał tylko na wersjach Pythona przed 3), lub tylko czysta wersja (jeśli będziesz działał tylko na wersjach Python zaczynających się od 3).Przykładowe użycie:
Po dołączeniu zobaczysz wyjątek zgłoszony w drugim wątku.
Jeśli używasz
six
lub tylko w języku Python 3, możesz poprawić informacje śledzenia stosu, które otrzymujesz po ponownym zgłoszeniu wyjątku. Zamiast samego stosu w punkcie łączenia możesz zawinąć wyjątek wewnętrzny w nowy wyjątek zewnętrzny i uzyskać oba ślady stosu za pomocąlub
źródło
Chociaż nie jest możliwe bezpośrednie złapanie wyjątku zgłoszonego w innym wątku, oto kod, aby dość transparentnie uzyskać coś bardzo zbliżonego do tej funkcjonalności. Wątek podrzędny musi podklasować
ExThread
klasę zamiast,threading.Thread
a wątek nadrzędny musi wywoływaćchild_thread.join_with_exception()
metodę zamiastchild_thread.join()
czekać na zakończenie wątku.Szczegóły techniczne tej implementacji: gdy wątek potomny zgłasza wyjątek, jest przekazywany do rodzica przez a
Queue
i ponownie wrzucany do wątku rodzica. Zauważ, że w tym podejściu nie ma żadnych zajęć.źródło
BaseException
, prawdaException
? Wszystko, co robisz, to propagowanie wyjątku z jednegoThread
do drugiego. W tej chwili IE aKeyboardInterrupt
byłoby cicho ignorowane, gdyby zostało podniesione w wątku tła.join_with_exception
zawiesza się w nieskończoność, jeśli zostanie wywołany drugi raz w martwym wątku. Poprawka: github.com/fraserharris/threading-extensions/blob/master/…Queue
konieczne; zobacz mój komentarz do odpowiedzi @ Świętego Mikołaja. Możesz to uprościć do czegoś takiego jak odpowiedź Rok Strniša poniżej stackoverflow.com/a/12223550/126362Jeśli wyjątek występuje w wątku, najlepszym sposobem jest ponowne podniesienie go w wątku wywołującym podczas
join
. Możesz uzyskać informacje o aktualnie obsługiwanym wyjątku za pomocąsys.exc_info()
funkcji. Informacje te można po prostu przechowywać jako właściwość obiektu wątku do momentujoin
wywołania, w którym to momencie można je ponownie wywołać.Zauważ, że
Queue.Queue
(jak zasugerowano w innych odpowiedziach) nie jest konieczne w tym prostym przypadku, w którym wątek zgłasza maksymalnie 1 wyjątek i kończy się zaraz po zgłoszeniu wyjątku . Unikamy warunków wyścigu, po prostu czekając na zakończenie wątku.Na przykład rozszerz
ExcThread
(poniżej), zastępującexcRun
(zamiastrun
).Python 2.x:
Python 3.x:
Formularz 3 argumentu dla
raise
zniknął w Pythonie 3, więc zmień ostatni wiersz na:źródło
concurrent.futures.as_completed
https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed
Następujące rozwiązanie:
Queue
Źródło:
Możliwe wyjście:
Niestety nie jest możliwe zabijanie kontraktów futures, aby anulować pozostałe, ponieważ jedna zawiedzie:
concurrent.features
; Python: concurrent.futures Jak to zrobić?threading
: Czy jest jakiś sposób na zabicie Wątku?Jeśli robisz coś takiego:
następnie
with
łapie go i czeka na zakończenie drugiego wątku przed kontynuowaniem. Poniższe zachowuje się podobnie:ponieważ
future.result()
ponownie podnosi wyjątek, jeśli taki wystąpił.Jeśli chcesz wyjść z całego procesu Pythona, możesz uciec
os._exit(0)
, ale prawdopodobnie oznacza to, że potrzebujesz refaktora.Klasa niestandardowa z doskonałą semantyką wyjątków
Skończyło się na tym, że napisałem dla siebie idealny interfejs: Właściwy sposób na ograniczenie maksymalnej liczby wątków działających jednocześnie? sekcja „Przykład kolejki z obsługą błędów”. Ta klasa ma być zarówno wygodna, jak i dać ci całkowitą kontrolę nad przesyłaniem i obsługą wyników / błędów.
Testowane na Python 3.6.7, Ubuntu 18.04.
źródło
To był bardzo paskudny mały problem i chciałbym wrzucić moje rozwiązanie. Niektóre inne rozwiązania, które znalazłem (na przykład async.io), wyglądały obiecująco, ale także prezentowały trochę czarną skrzynkę. Podejście do kolejki / zdarzenia wiąże się z pewną implementacją. Jednoczesny kod źródłowy futures ma jednak tylko około 1000 linii i jest łatwy do zrozumienia . Pozwoliło mi to łatwo rozwiązać problem: tworzyć wątki robocze ad-hoc bez dużej konfiguracji i móc wychwytywać wyjątki w głównym wątku.
Moje rozwiązanie wykorzystuje współbieżny interfejs futures API i wątkowy interfejs API. Pozwala ci stworzyć pracownika, który da ci zarówno wątek, jak i przyszłość. W ten sposób możesz dołączyć do wątku, aby czekać na wynik:
... lub możesz pozwolić pracownikowi wysłać oddzwonienie po zakończeniu:
... lub możesz zapętlać, aż wydarzenie się zakończy:
Oto kod:
... i funkcja testowa:
źródło
Jako noobie do wątków zajęło mi dużo czasu, aby zrozumieć, jak zaimplementować kod Mateusza Kobosa (powyżej). Oto wyjaśniona wersja, która pomoże zrozumieć, jak z niej korzystać.
źródło
Podobnie jak RickardSjogren bez kolejki, sys itp., Ale także bez niektórych detektorów sygnałów: bezpośrednio uruchom moduł obsługi wyjątków, który odpowiada blokowi wyjątków.
Tylko self._callback i blok wyjątków w run () stanowią uzupełnienie normalnego wątku.
źródło
Wiem, że jestem trochę spóźniony na imprezę, ale miałem bardzo podobny problem, ale obejmował on użycie tkintera jako GUI, a mainloop uniemożliwił użycie dowolnego rozwiązania zależnego od .join (). Dlatego dostosowałem rozwiązanie podane w EDIT pierwotnego pytania, ale uczyniłem to bardziej ogólnym, aby ułatwić zrozumienie dla innych.
Oto nowa klasa wątków w akcji:
Oczywiście zawsze możesz sprawić, by obsługiwał wyjątek w inny sposób niż logowanie, na przykład wydrukowanie go lub wysłanie do konsoli.
Dzięki temu możesz używać klasy ExceptionThread dokładnie tak samo, jak klasy Thread, bez żadnych specjalnych modyfikacji.
źródło
Jedna metoda, którą lubię, opiera się na wzorcu obserwatora . Definiuję klasę sygnału, której mój wątek używa do wysyłania wyjątków dla słuchaczy. Można go również użyć do zwrócenia wartości z wątków. Przykład:
Nie mam wystarczającego doświadczenia w pracy z wątkami, aby stwierdzić, że jest to całkowicie bezpieczna metoda. Ale zadziałało dla mnie i podoba mi się elastyczność.
źródło
Używanie nagich wyjątków nie jest dobrą praktyką, ponieważ zwykle łapiesz więcej, niż się spodziewasz.
Sugerowałbym modyfikację,
except
aby ŁĄCZYĆ TYLKO wyjątek, który chcesz obsłużyć. Nie sądzę, aby podniesienie go miało pożądany efekt, ponieważ jeśli przejdziesz do tworzenia instancjiTheThread
na zewnątrztry
, jeśli pojawi się wyjątek, zadanie nigdy się nie wydarzy.Zamiast tego możesz po prostu zaalarmować i przejść dalej, na przykład:
Następnie, gdy ten wyjątek zostanie złapany, możesz sobie z nim poradzić. Wtedy, gdy zewnętrzny
try
wychwytuje wyjątekTheThread
, wiesz, że nie będzie to ten, z którym już sobie poradziłeś, i pomoże ci odizolować przebieg procesu.źródło
Prostym sposobem wychwycenia wyjątku wątku i przekazania go z powrotem do metody wywołującej może być przekazanie słownika lub listy do
worker
metody.Przykład (przekazanie słownika do metody roboczej):
źródło
Zawijaj wątek z przechowywaniem wyjątków.
źródło
pygolang zapewnia sync.WorkGroup, która w szczególności propaguje wyjątek od spawnowanych wątków roboczych do głównego wątku. Na przykład:
daje następujące po uruchomieniu:
Oryginalny kod z pytania byłby po prostu:
źródło