Próbuję zrozumieć wątki w Pythonie. Przejrzałem dokumentację i przykłady, ale szczerze mówiąc, wiele przykładów jest zbyt wyrafinowanych i mam problem z ich zrozumieniem.
Jak wyraźnie pokazać zadania dzielone na wiele wątków?
Próbuję zrozumieć wątki w Pythonie. Przejrzałem dokumentację i przykłady, ale szczerze mówiąc, wiele przykładów jest zbyt wyrafinowanych i mam problem z ich zrozumieniem.
Jak wyraźnie pokazać zadania dzielone na wiele wątków?
Odpowiedzi:
Ponieważ pytanie to zostało zadane w 2010 r., Wprowadzono proste uproszczenie sposobu wykonywania wielowątkowości za pomocą Pythona z mapą i pulą .
Poniższy kod pochodzi z artykułu / postu na blogu, który zdecydowanie powinieneś sprawdzić (bez powiązania) - Równoległość w jednym wierszu: lepszy model codziennych zadań gwintowania . Podsumuję poniżej - ostatecznie jest to tylko kilka wierszy kodu:
Która jest wielowątkową wersją:
Opis
Realizacja
multiprocessing.dummy
jest dokładnie taki sam jak moduł wieloprocesowy, ale zamiast tego używa wątków ( ważne rozróżnienie - użyj wielu procesów do zadań wymagających dużej mocy procesora; wątki do (i podczas) operacji we / wy :A wyniki pomiaru czasu:
Przekazywanie wielu argumentów (działa tak tylko w Pythonie 3.3 i nowszych ):
Aby przekazać wiele tablic:
Lub przekazać stałą i tablicę:
Jeśli używasz wcześniejszej wersji Pythona, możesz przekazać wiele argumentów za pomocą tego obejścia ).
(Podziękowania dla user136036 za pomocny komentarz.)
źródło
with Pool(8) as p: p.map( *whatever* )
i pozbywać się linii księgowych.Oto prosty przykład: musisz wypróbować kilka alternatywnych adresów URL i zwrócić zawartość pierwszego, aby odpowiedzieć.
Jest to przypadek, w którym wątki są używane jako prosta optymalizacja: każdy wątek oczekuje na adres URL do rozwiązania i odpowiedzi, aby umieścić jego zawartość w kolejce; każdy wątek jest demonem (nie utrzyma procesu, jeśli główny wątek się skończy - jest to częstsze niż nie); główny wątek rozpoczyna wszystkie wątki, wykonuje
get
kolejkę, aby poczekać, aż jeden z nich wykona aput
, a następnie emituje wyniki i kończy działanie (co powoduje usunięcie wszystkich wątków, które mogą być nadal uruchomione, ponieważ są one wątkami demonicznymi).Prawidłowe użycie wątków w Pythonie jest niezmiennie połączone z operacjami We / Wy (ponieważ CPython i tak nie używa wielu rdzeni do uruchamiania zadań związanych z procesorem, jedynym powodem wątkowania nie jest blokowanie procesu podczas oczekiwania na niektóre operacje we / wy ). Nawiasem mówiąc, kolejki są prawie zawsze najlepszym sposobem na wyodrębnienie pracy do wątków i / lub zebranie wyników pracy, i są wewnętrznie bezpieczne dla wątków, dzięki czemu nie musisz się martwić o blokady, warunki, zdarzenia, semafory i inne inter -przeczytane koncepcje koordynacji / komunikacji.
źródło
join()
metody, ponieważ sprawiłoby to, że główny wątek czekałby, aż zostaną wykonane bez ciągłego zużywania procesora sprawdzanie wartości. @Alex: dzięki, właśnie tego potrzebowałem, aby zrozumieć, jak używać wątków.Queue
nazwę modułu naqueue
. Nazwa metody jest taka sama.s = q.get()
print s
@ krs013 Nie potrzebujesz,join
ponieważ Queue.get () blokuje.UWAGA : Do faktycznej równoległości w Pythonie należy użyć modułu wieloprocesowego do rozwidlenia wielu procesów, które są wykonywane równolegle (ze względu na globalną blokadę interpretera wątki Pythona zapewniają przeplatanie, ale w rzeczywistości są wykonywane szeregowo, a nie równolegle i są tylko przydatne podczas przeplatania operacji we / wy).
Jeśli jednak szukasz jedynie przeplotu (lub wykonujesz operacje we / wy, które można zrównoleglić pomimo globalnej blokady interpretera), możesz zacząć od modułu wątków . Jako bardzo prosty przykład rozważmy problem sumowania dużego zakresu przez równoległe sumowanie podzakresów:
Zauważ, że powyższy jest bardzo głupim przykładem, ponieważ absolutnie nie ma żadnych operacji we / wy i będzie wykonywany szeregowo, choć przeplatany (z dodatkowym narzutem przełączania kontekstu) w CPython z powodu globalnej blokady interpretera.
źródło
thread1
działa, dopóki nie zostanie zakończone, gdy główny wątek blokuje się, wtedy dzieje się to samothread2
, a następnie główny wątek wznawia się i drukuje zgromadzone wartości.super(SummingThread, self).__init__()
? Jak w stackoverflow.com/a/2197625/806988Jak inni wspomniano, CPython może używać wątków tylko do oczekiwań we / wy z powodu GIL .
Jeśli chcesz korzystać z wielu rdzeni dla zadań związanych z procesorem, użyj przetwarzania wieloprocesowego :
źródło
f
. Równolegle program główny po prostu czeka na zakończenie procesu, cojoin
jest z nim zgodne. Jeśli główna część właśnie się zakończyła, podproces może, ale nie musi, działać do końca, więc robienie tegojoin
zawsze jest zalecane.map
funkcję znajduje się tutaj: stackoverflow.com/a/28463266/2327328Tylko uwaga: kolejkowanie nie jest wymagane do tworzenia wątków.
To najprostszy przykład, jaki mogłem sobie wyobrazić, który pokazuje 10 procesów działających równolegle.
źródło
for
pętli, możesz wywoływaćthread.start()
w pierwszej pętli.Odpowiedź Alexa Martellego pomogła mi. Jednak tutaj jest zmodyfikowana wersja, która moim zdaniem była bardziej przydatna (przynajmniej dla mnie).
Zaktualizowano: działa zarówno w Pythonie 2, jak i Pythonie 3
źródło
import Queue ModuleNotFoundError: No module named 'Queue'
działam w Pythonie 3.6.5 niektóre posty wspominają, że w Pythonie 3.6.5 jest,queue
ale nawet po jego zmianie nadal nie działaBiorąc pod uwagę funkcję
f
, napisz to w ten sposób:Aby przekazać argumenty do
f
źródło
Thread
obiekt oczyszcza się. Zobacz dokumenty . Istniejeis_alive()
metoda, której można użyć do sprawdzenia wątku, jeśli zajdzie taka potrzeba.is_alive
metodę, ale nie mogłem wymyślić, jak zastosować ją do wątku. Próbowałem przypisać,thread1=threading.Thread(target=f).start()
a następnie sprawdzić tothread1.is_alive()
, alethread1
jest wypełnioneNone
, więc nie ma szczęścia. Czy wiesz, czy istnieje inny sposób uzyskania dostępu do wątku?thread1=threading.Thread(target=f)
następniethread1.start()
. To możesz zrobićthread1.is_alive()
.thread1.is_alive()
zwrotami,False
jak tylko funkcja wyjdzie.Uznałem to za bardzo przydatne: utwórz tyle wątków, ile rdzeni i pozwól im wykonywać (dużą) liczbę zadań (w tym przypadku wywoływanie programu powłoki):
źródło
Python 3 ma możliwość uruchamiania równoległych zadań . Ułatwia to naszą pracę.
Ma pule wątków i pule procesów .
Poniżej przedstawiono wgląd:
ThreadPoolExecutor Przykład ( źródło )
ProcessPoolExecutor ( źródło )
źródło
Korzystanie z nowego modułu concurrent.futures
Podejście do executorów może wydawać się znajome dla wszystkich, którzy wcześniej brudzili sobie ręce Javą.
Na marginesie: Aby zachować rozsądek we wszechświecie, nie zapomnij zamknąć puli / executorów, jeśli nie używasz
with
kontekstu (co jest tak niesamowite, że robi to za Ciebie)źródło
Dla mnie doskonałym przykładem wątków jest monitorowanie zdarzeń asynchronicznych. Spójrz na ten kod.
Możesz grać z tym kodem, otwierając sesję IPython i robiąc coś takiego:
Zaczekaj kilka minut
źródło
Większość dokumentacji i samouczków używa Pythona
Threading
iQueue
modułu i mogą one wydawać się przytłaczające dla początkujących.Być może rozważ
concurrent.futures.ThreadPoolExecutor
moduł Python 3.W połączeniu z
with
klauzulami i listami może to być prawdziwy urok.źródło
Widziałem tutaj wiele przykładów, w których nie wykonywano żadnej prawdziwej pracy, i były one głównie związane z procesorem. Oto przykład zadania związanego z procesorem, które oblicza wszystkie liczby pierwsze między 10 milionów a 10,05 miliona. Użyłem tutaj wszystkich czterech metod:
Oto wyniki na moim czterordzeniowym komputerze Mac OS X.
źródło
if __name__ == '__main__':
przed głównym wezwanie, w przeciwnym razie ikra siebie i drukuje pomiarowe Próba zostało wykonane, aby rozpocząć nowy proces przed ... .Oto bardzo prosty przykład importu CSV za pomocą wątków. (Włączenie biblioteki może się różnić w zależności od celu).
Funkcje pomocnicze:
Funkcja kierowcy:
źródło
Chciałbym przedstawić prosty przykład i wyjaśnienia, które okazały się przydatne, gdy sam musiałem rozwiązać ten problem.
W tej odpowiedzi znajdziesz informacje o GIL Pythona (globalna blokada interpretera) i prosty przykład z dnia na dzień napisany przy użyciu multiprocessing.dummy oraz kilka prostych testów porównawczych.
Global Interpreter Lock (GIL)
Python nie pozwala na wielowątkowość w najprawdziwszym tego słowa znaczeniu. Ma pakiet wielowątkowy, ale jeśli chcesz wielowątkowy, aby przyspieszyć swój kod, zwykle nie jest dobrym pomysłem, aby go użyć.
Python ma konstrukcję zwaną globalną blokadą interpretera (GIL). GIL zapewnia, że tylko jeden z twoich „wątków” może być wykonywany jednocześnie. Wątek nabywa GIL, wykonuje niewielką pracę, a następnie przekazuje GIL do następnego wątku.
Dzieje się to bardzo szybko, więc dla ludzkiego oka może się wydawać, że twoje wątki wykonują się równolegle, ale tak naprawdę po prostu korzystają z tego samego rdzenia procesora.
Całe to przekazywanie GIL zwiększa koszty wykonania. Oznacza to, że jeśli chcesz, aby Twój kod działał szybciej, korzystanie z pakietu wątków często nie jest dobrym pomysłem.
Istnieją powody, aby używać pakietu wątków Pythona. Jeśli chcesz uruchomić kilka rzeczy jednocześnie, a wydajność nie jest problemem, to jest całkowicie w porządku i wygodne. Lub jeśli korzystasz z kodu, który musi na coś czekać (np. Niektóre wejścia / wyjścia), może to mieć sens. Ale biblioteka wątków nie pozwala na użycie dodatkowych rdzeni procesora.
Wielowątkowość można zlecić systemowi operacyjnemu (poprzez przetwarzanie wieloskładnikowe) i niektórym zewnętrznym aplikacjom, które wywołują Twój kod Python (na przykład Spark lub Hadoop ), lub niektóre kody, które wywołuje Twój kod Python (na przykład: możesz twój kod Python wywołuje funkcję C, która wykonuje drogie wielowątkowe rzeczy).
Dlaczego to ma znaczenie
Ponieważ wiele osób spędza dużo czasu próbując znaleźć wąskie gardła w swoim fantazyjnym wielowątkowym kodzie Pythona, zanim dowiedzą się, czym jest GIL.
Gdy te informacje będą jasne, oto mój kod:
źródło
Oto wielowątkowość z prostym przykładem, który będzie pomocny. Możesz go uruchomić i łatwo zrozumieć, jak działa wielowątkowość w Pythonie. Użyłem blokady, aby uniemożliwić dostęp do innych wątków, dopóki poprzednie wątki nie zakończyły swojej pracy. Korzystając z tego wiersza kodu,
możesz zezwolić na wiele procesów na raz i trzymać resztę wątków, które będą uruchamiane później lub po zakończeniu poprzednich procesów.
źródło
Pożyczając z tego postu wiemy o wyborze między wielowątkowością, wieloprocesowością i asynchronią /
asyncio
i ich wykorzystaniem.Python 3 ma nową wbudowaną bibliotekę w celu współbieżności i równoległości: concurrent.futures
Pokażę więc poprzez eksperyment, aby uruchomić cztery zadania (tj.
.sleep()
Metodę) wedługThreading-Pool
sposobu:Wynik:
[ UWAGA ]:
multiprocessing
vsthreading
) można zmienićThreadPoolExecutor
naProcessPoolExecutor
.źródło
Żadne z poprzednich rozwiązań nie używało wielu rdzeni na moim serwerze GNU / Linux (gdzie nie mam uprawnień administratora). Po prostu działały na jednym rdzeniu.
Użyłem
os.fork
interfejsu niższego poziomu do odrodzenia wielu procesów. Oto kod, który zadziałał dla mnie:źródło
źródło