Czy Python obsługuje wielowątkowość? Czy może przyspieszyć czas wykonania?


98

Jestem trochę zdezorientowany, czy wielowątkowość działa w Pythonie, czy nie.

Wiem, że było wiele pytań na ten temat i przeczytałem wiele z nich, ale nadal jestem zdezorientowany. Wiem z własnego doświadczenia i widziałem, jak inni publikują własne odpowiedzi i przykłady tutaj na StackOverflow, że wielowątkowość jest rzeczywiście możliwa w Pythonie. Dlaczego więc wszyscy powtarzają, że Python jest zablokowany przez GIL i tylko jeden wątek może działać na raz? To oczywiście działa. A może jest jakaś różnica, której tu nie dostanę?

Wielu plakatów / respondentów wciąż wspomina, że ​​wątkowanie jest ograniczone, ponieważ nie wykorzystuje wielu rdzeni. Powiedziałbym jednak, że są one nadal przydatne, ponieważ działają jednocześnie, a tym samym szybciej wykonują połączone obciążenie pracą. Mam na myśli, dlaczego w przeciwnym razie miałby istnieć moduł wątku Pythona?

Aktualizacja:

Dzięki za wszystkie dotychczasowe odpowiedzi. Rozumiem, że wielowątkowość będzie działać tylko równolegle dla niektórych zadań we / wy, ale może działać tylko jedno na raz w przypadku zadań wielordzeniowych związanych z procesorem.

Nie jestem do końca pewien, co to oznacza dla mnie w praktyce, więc podam tylko przykład zadania, które chciałbym wykonać wielowątkowo. Na przykład, powiedzmy, że chcę przejrzeć bardzo długą listę ciągów i wykonać kilka podstawowych operacji na każdym elemencie listy. Jeśli podzielę listę, wyślę każdą podlistę do przetworzenia przez mój kod pętli / łańcucha w nowym wątku i odeślę wyniki z powrotem do kolejki, czy te obciążenia będą działać mniej więcej w tym samym czasie? Co najważniejsze, czy to teoretycznie przyspieszy czas potrzebny do uruchomienia skryptu?

Innym przykładem może być to, że mogę wyrenderować i zapisać cztery różne obrazy za pomocą PIL w czterech różnych wątkach i czy będzie to szybsze niż przetwarzanie obrazów jeden po drugim? Wydaje mi się, że naprawdę się zastanawiam nad tym elementem szybkości, a nie nad prawidłową terminologią.

Wiem również o module przetwarzania wieloprocesowego, ale moim głównym zainteresowaniem w tej chwili są małe i średnie obciążenia zadań (10-30 sekund), więc myślę, że wielowątkowość będzie bardziej odpowiednia, ponieważ podprocesy mogą być inicjowane wolno.


4
To dość załadowane pytanie. Myślę, że odpowiedź leży w tym , co chcesz, aby nici robiły. W większości przypadków GIL zapobiega jednoczesnemu uruchomieniu więcej niż 1 wątku. Jest jednak kilka przypadków, w których GIL jest udostępniany (np. Odczyt z pliku), tak aby można było to zrobić równolegle. Należy również zauważyć, że GIL jest szczegółem implementacyjnym Cpythona (najpowszechniejsza implementacja). Żadna inna implementacja Pythona (Jython, PyPy itp.) Nie ma GIL (AFAIK)
mgilson

2
@mgilson PyPy ma GIL.

2
@delnan - Wygląda na to, że masz rację. Dzięki.
mgilson

1
„Podprocesy mogą być inicjowane powoli” - można utworzyć pulę zadań gotowych do wykonania. Narzut można ograniczyć do w przybliżeniu czasu potrzebnego do serializacji / deserializacji danych wymaganych do rozpoczęcia pracy zadania.
Brian Cain,

1
@KarimBahgat, dokładnie to mam na myśli.
Brian Cain

Odpowiedzi:


136

GIL nie zapobiega gwintowaniu. Wszystko, co robi GIL, to upewnienie się, że tylko jeden wątek wykonuje jednocześnie kod Pythona; control nadal przełącza się między wątkami.

Tym, czego zapobiega GIL, jest wykorzystywanie więcej niż jednego rdzenia procesora lub oddzielnych procesorów do równoległego uruchamiania wątków.

Dotyczy to tylko kodu w Pythonie. Rozszerzenia C mogą i faktycznie zwalniają GIL, aby umożliwić działanie wielu wątków kodu C i jednego wątku Pythona na wielu rdzeniach. Rozciąga się to na operacje we / wy kontrolowane przez jądro, takie jak select()wywołania odczytów i zapisów z gniazda, dzięki czemu Python obsługuje zdarzenia sieciowe w rozsądny sposób w wielowątkowej konfiguracji wielordzeniowej.

To, co robi wiele wdrożeń serwerów, to uruchamianie więcej niż jednego procesu Pythona, aby umożliwić systemowi operacyjnemu zarządzanie harmonogramem między procesami w celu maksymalnego wykorzystania rdzeni procesora. multiprocessingBiblioteki można również używać do obsługi przetwarzania równoległego w wielu procesach z jednej bazy kodu i procesu nadrzędnego, jeśli odpowiada to Twoim przypadkom użycia.

Zwróć uwagę, że GIL ma zastosowanie tylko do implementacji CPythona; Jython i IronPython używają innej implementacji wątków (odpowiednio natywnych wspólnych wątków Java VM i .NET).

Aby bezpośrednio zająć się aktualizacją: Każde zadanie, które próbuje uzyskać przyspieszenie z wykonywania równoległego przy użyciu czystego kodu Pythona, nie zobaczy przyspieszenia, ponieważ kod Pythona z wątkami jest zablokowany do jednego wątku wykonywanego na raz. Jeśli jednak mieszasz rozszerzenia C i operacje we / wy (takie jak operacje PIL lub numpy), każdy kod C może działać równolegle z jednym aktywnym wątkiem Pythona.

Obsługa wątków w Pythonie doskonale nadaje się do tworzenia responsywnego interfejsu GUI lub do obsługi wielu krótkich żądań sieci Web, w których operacje wejścia / wyjścia są bardziej wąskim gardłem niż kod Pythona. Nie nadaje się do zrównoleglania kodu Pythona wymagającego dużej mocy obliczeniowej, trzymania się multiprocessingmodułu w przypadku takich zadań lub delegowania do dedykowanej biblioteki zewnętrznej.


Dzięki @MartijnPieters, mam jaśniejszą odpowiedź na moje pytanie, czy wątki mogą być używane do przyspieszenia kodu, takiego jak pętla for, która brzmi „nie”. Może ty lub ktoś mógłby napisać nową odpowiedź, którą zaakceptuję, która zawiera kilka konkretnych przykładów typowych modułów / kodów / operacji, w których GIL zezwoli na wątkowanie równolegle, a zatem szybciej (np. Przykłady tych operacji we / wy i sieci / operacje odczytu gniazda, które zostały wspomniane, oraz wszelkie inne przypadki, w których wielowątkowość w Pythonie jest przydatna). Może ładna lista typowych zastosowań wielowątkowych i kilka przykładów programowania, jeśli to możliwe?
Karim Bahgat,

4
Nie, nie sądzę, żeby taka odpowiedź była bardzo pomocna; szczerze. Nie możesz nigdy stworzyć wyczerpującej listy, ale ogólna zasada jest taka, że ​​każde wejście / wyjście (odczyt i zapis plików, gniazda sieciowe, potoki) jest obsługiwane w C, a wiele bibliotek C również wypuszcza GIL dla swoich operacje, ale to biblioteki muszą to udokumentować.
Martijn Pieters

1
Mój zły, do tej pory nie widziałem zaktualizowanej odpowiedzi, w której podałeś kilka fajnych przykładów użycia wątku. Obejmowały one (popraw mnie, jeśli się mylę) programowanie sieciowe (np. urllib.urlopen()?), Image.transform()Wywoływanie jednego skryptu Pythona z poziomu GUI Pythona i wywoływanie wielu numpy.array()operacji PIL (np. ) I numpy (np. ) Z wątkami. W swoim komentarzu podałeś więcej przykładów, takich jak używanie wielu wątków do odczytywania plików (np. f.read()?). Wiem, że wyczerpująca lista nie jest możliwa, chciałem tylko typy przykładów, które podałeś w swojej aktualizacji. Tak czy inaczej, zaakceptowałem twoją odpowiedź :)
Karim Bahgat

2
@KarimBahgat: Tak, urllib.urlopen()wywoływałby gniazda sieciowe, oczekiwanie na wejście / wyjście gniazda to doskonała okazja do przełączania wątków i zrobienia czegoś innego.
Martijn Pieters

4
Chociaż nie jest to bezpośrednio związane z tym problemem, warto zauważyć, że czasami tworzenie wątków wcale nie dotyczy wydajności; prościej może być napisanie kodu jako wielu niezależnych wątków wykonania. Na przykład możesz mieć jeden wątek odtwarzający muzykę w tle, jeden obsługujący interfejs użytkownika, a drugi zajmujący się obliczeniami, które w końcu muszą zostać wykonane, ale nie są w pośpiechu. Próba sekwencjonowania odtwarzania następnego bufora audio za pomocą pętli UI lub rozbicia obliczeń na wystarczająco małe fragmenty, aby nie zakłócać interaktywności, może być znacznie trudniejsza niż używanie wątków.
abarnert

4

Tak. :)

Masz moduł gwintowania niskiego poziomu i moduł gwintowania wyższego poziomu . Ale jeśli chcesz po prostu używać maszyn wielordzeniowych, wieloprocesorowych jest moduł .

Cytat z dokumentów :

W CPythonie, ze względu na blokadę Global Interpreter Lock, tylko jeden wątek może wykonywać kod Pythona na raz (nawet jeśli niektóre biblioteki zorientowane na wydajność mogą przezwyciężyć to ograniczenie). Jeśli chcesz, aby Twoja aplikacja lepiej wykorzystywała zasoby obliczeniowe maszyn wielordzeniowych, zalecamy użycie przetwarzania wieloprocesowego. Jednak wątkowanie jest nadal odpowiednim modelem, jeśli chcesz jednocześnie uruchamiać wiele zadań związanych z we / wy.


3

W Pythonie dozwolone jest tworzenie wątków, jedynym problemem jest to, że GIL upewni się, że tylko jeden wątek jest wykonywany na raz (bez równoległości).

Więc w zasadzie, jeśli chcesz wielowątkowość kodu w celu przyspieszenia obliczeń, nie przyspieszy to, ponieważ tylko jeden wątek jest wykonywany na raz, ale jeśli użyjesz go na przykład do interakcji z bazą danych, to zrobi.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.