Poniższa funkcja foo
zwraca ciąg znaków 'foo'
. Jak mogę uzyskać wartość 'foo'
zwracaną z celu wątku?
from threading import Thread
def foo(bar):
print('hello {}'.format(bar))
return 'foo'
thread = Thread(target=foo, args=('world!',))
thread.start()
return_value = thread.join()
Przedstawiony powyżej „oczywisty sposób na zrobienie tego” nie działa: thread.join()
powrócił None
.
futures = [executor.submit(foo, param) for param in param_list]
Kolejność zostanie utrzymana, a zamknięciewith
umożliwi zbieranie wyników.[f.result() for f in futures]
FWIW,
multiprocessing
moduł ma ładny interfejs do tego przy użyciuPool
klasy. A jeśli chcesz pozostać przy wątkach, a nie procesach, możesz po prostu użyć tejmultiprocessing.pool.ThreadPool
klasy jako zamiennika.źródło
multiprocess
, nie mają one nic wspólnego z procesami.processes=1
więcej niż jednego, jeśli masz więcej wątków!Jednym ze sposobów, które widziałem, jest przekazanie modyfikowalnego obiektu, takiego jak lista lub słownik, do konstruktora wątku wraz z indeksem lub innym identyfikatorem. Wątek może następnie zapisać wyniki w dedykowanym gnieździe w tym obiekcie. Na przykład:
Jeśli naprawdę chcesz
join()
zwrócić zwracaną wartość wywoływanej funkcji, możesz to zrobić za pomocąThread
podklasy, takiej jak poniżej:To staje się trochę owłosione z powodu zmiany nazwy i uzyskuje dostęp do „prywatnych” struktur danych specyficznych dla
Thread
implementacji… ale działa.Dla python3
źródło
threading
, a nie inną bibliotekę do wypróbowania, a ograniczenie wielkości puli wprowadza dodatkowy potencjalny problem, który miał miejsce w moim przypadku.TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given
. Jakiś sposób to naprawić?_Thread__target
rzecz). Sprawisz, że każdy, kto będzie próbował przenieść Twój kod do Pythona 3, będzie cię nienawidził, dopóki nie zorientuje się, co zrobiłeś (z powodu używania nieudokumentowanych funkcji, które zmieniły się między 2 a 3). Dobrze udokumentuj swój kod.Odpowiedź Jake'a jest dobra, ale jeśli nie chcesz korzystać z puli wątków (nie wiesz, ile wątków będziesz potrzebować, ale utwórz je w razie potrzeby), dobrym sposobem na przesyłanie informacji między wątkami jest wbudowane Klasa Queue.Queue , ponieważ zapewnia bezpieczeństwo wątków.
Utworzyłem następujący dekorator, aby działał podobnie do puli wątków:
Następnie używasz go jako:
Udekorowana funkcja tworzy nowy wątek za każdym razem, gdy jest wywoływana, i zwraca obiekt Thread zawierający kolejkę, która otrzyma wynik.
AKTUALIZACJA
Minęło sporo czasu, odkąd opublikowałem tę odpowiedź, ale wciąż uzyskuje widoki, więc pomyślałem, że zaktualizuję ją, aby odzwierciedlić sposób, w jaki to robię w nowszych wersjach Pythona:
Dodano
concurrent.futures
moduł Python 3.2, który zapewnia interfejs wysokiego poziomu do zadań równoległych. ZapewniaThreadPoolExecutor
iProcessPoolExecutor
, dzięki czemu można używać puli wątków lub procesów z tym samym interfejsem API.Jedną z zalet tego interfejsu API jest to, że przekazanie zadania do obiektu
Executor
zwracaFuture
obiekt, który zostanie uzupełniony o wartość zwracaną przez użytkownika.To sprawia, że dołączanie
queue
obiektu nie jest konieczne, co znacznie upraszcza dekorator:Spowoduje to użycie domyślnego modułu wykonującego pulę wątków, jeśli nie zostanie przekazany.
Użycie jest bardzo podobne do wcześniejszego:
Jeśli używasz Python 3.4+, jeden naprawdę miłą cechę tą metodą (i Przyszłość obiektów w ogóle) jest to, że przyszłość może być zwrócony zawinięte do przekształcić go
asyncio.Future
zasyncio.wrap_future
. To sprawia, że łatwo współpracuje z coroutines:Jeśli nie potrzebujesz dostępu do
concurrent.Future
obiektu bazowego , możesz dołączyć zawijanie do dekoratora:Następnie, ilekroć chcesz zepchnąć intensywny procesor lub blokować kod z wątku pętli zdarzeń, możesz umieścić go w ozdobnej funkcji:
źródło
AttributeError: 'module' object has no attribute 'Lock'
Pojawia się błąd stwierdzający, że wydaje się, że pochodzi z liniiy = long_task(10)
... myśli?Inne rozwiązanie, które nie wymaga zmiany istniejącego kodu:
Można go również łatwo dostosować do środowiska wielowątkowego:
źródło
from queue import Queue
.Parris / kindall's odpowiedź
join
/return
odpowiedź przeniesiona do Pythona 3:Uwaga:
Thread
klasa jest zaimplementowana inaczej w Pythonie 3.źródło
Ukradłem odpowiedź życzliwą i trochę ją posprzątałem.
Kluczową częścią jest dodanie * args i ** kwargs do join () w celu obsłużenia limitu czasu
ZAKTUALIZOWANO ODPOWIEDŹ PONIŻEJ
To moja najpopularniejsza odpowiedź, więc postanowiłem zaktualizować kod, który będzie działał zarówno na py2, jak i py3.
Dodatkowo widzę wiele odpowiedzi na to pytanie, które pokazują brak zrozumienia odnośnie Thread.join (). Niektóre całkowicie nie radzą sobie z
timeout
arg. Ale jest też przypadek narożny, o którym powinieneś wiedzieć, jeśli masz (1) funkcję docelową, która może zwrócićNone
i (2) przekazujesz równieżtimeout
argument arg, aby dołączyć (). Zobacz „TEST 4”, aby zrozumieć ten przypadek narożny.Klasa ThreadWithReturn, która działa z py2 i py3:
Niektóre przykładowe testy pokazano poniżej:
Czy potrafisz zidentyfikować przypadek, w którym możemy spotkać się z TESTEM 4?
Problem polega na tym, że oczekujemy, że metoda returnMe () zwróci None (patrz TEST 2), ale spodziewamy się również, że funkcja join () zwróci None, jeśli upłynie limit czasu.
returned is None
oznacza albo:(1) właśnie to zwrócił returnMe () lub
(2) Upłynął limit czasu dołączenia ()
Ten przykład jest trywialny, ponieważ wiemy, że giveMe () zawsze zwróci None. Ale w rzeczywistej instancji (gdzie cel może zgodnie z prawem zwrócić Brak lub coś innego) chcielibyśmy wyraźnie sprawdzić, co się stało.
Poniżej znajduje się sposób rozwiązania tego przypadku:
źródło
target
,args
ikwargs
argumentów init, jak zmienne składowe w swojej klasie.Korzystanie z kolejki:
źródło
out_queue1
trzeba będzie pętli nadout_queue1.get()
i złapać wyjątek Queue.Empty:ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass
. Średniki do symulacji przerwania linii.Moim rozwiązaniem tego problemu jest zawinięcie funkcji i wątku w klasę. Nie wymaga używania pul, kolejek ani przekazywania zmiennych typu c. To również nie blokuje. Zamiast tego sprawdzasz status. Zobacz przykład, jak go używać na końcu kodu.
źródło
join
zawsze zwracajNone
, myślę, że powinieneś podklasęThread
obsługiwać kody powrotu i tak dalej.źródło
Biorąc pod uwagę @iman komentarzu @JakeBiesinger odpowiedź mam kompozyty go mieć różną liczbę wątków:
Twoje zdrowie,
Chłopak.
źródło
Możesz zdefiniować zmienną powyżej zakresu funkcji wątkowej i dodać do tego wynik. (Zmodyfikowałem również kod, aby był zgodny z python3)
To wraca
{'world!': 'foo'}
Jeśli użyjesz wejścia funkcji jako klucza do wyników, każde unikalne wejście gwarantuje wpis w wynikach
źródło
Używam tego opakowania, które wygodnie włącza dowolną funkcję do uruchamiania w
Thread
- dbając o jej wartość zwracaną lub wyjątek. Nie dodajeQueue
kosztów ogólnych.Przykłady użycia
Uwagi na temat
threading
modułuWygodna obsługa zwracanych wartości i wyjątków funkcji wątkowej jest częstą potrzebą „Pythonic” i rzeczywiście powinna być już oferowana przez
threading
moduł - być może bezpośrednio wThread
klasie standardowej .ThreadPool
ma o wiele za dużo narzutów na proste zadania - 3 zarządzanie wątkami, dużo biurokracji. NiestetyThread
układ został pierwotnie skopiowany z Javy - co widać np. Z wciąż bezużytecznego parametru 1. (!) Konstruktoragroup
.źródło
Zdefiniuj cel, aby
1) wziąć argument
q
2) zastąpić dowolne instrukcje
return foo
zq.put(foo); return
więc funkcja
stanie się
i wtedy postępowałbyś jako taki
I możesz użyć dekoratorów / opakowań funkcji, aby móc używać istniejących funkcji
target
bez ich modyfikowania, ale postępuj zgodnie z tym podstawowym schematem.źródło
results = [ans_q.get() for _ in xrange(len(threads))]
Jak wspomniano, pula wieloprocesowa jest znacznie wolniejsza niż podstawowe wątki. Korzystanie z kolejek zaproponowanych w niektórych odpowiedziach tutaj jest bardzo skuteczną alternatywą. Używam go ze słownikami, aby móc uruchomić wiele małych wątków i odzyskać wiele odpowiedzi, łącząc je ze słownikami:
źródło
Idea GuySoft jest świetna, ale myślę, że obiekt niekoniecznie musi dziedziczyć po Thread, a start () można usunąć z interfejsu:
źródło
Jednym z typowych rozwiązań jest zawinięcie funkcji
foo
dekoratoremWtedy cały kod może tak wyglądać
Uwaga
Jedną ważną kwestią jest to, że zwracane wartości mogą być nieuporządkowane . (W rzeczywistości
return value
niekoniecznie są one zapisywane wqueue
, ponieważ możesz wybrać dowolną strukturę danych bezpieczną dla wątków )źródło
Dlaczego po prostu nie użyjesz zmiennej globalnej?
źródło
Odpowiedź Kindalla w Python3
źródło
Jeśli tylko prawda lub fałsz mają być sprawdzane na podstawie wywołania funkcji, prostszym rozwiązaniem, które znajduję, jest aktualizacja globalnej listy.
Jest to bardziej pomocne, gdy chcesz sprawdzić, czy któryś z wątków zwrócił fałszywy status, aby podjąć niezbędne działanie.
źródło