Dlaczego Global Interpreter Lock?

89

Jaka jest dokładnie funkcja globalnej blokady interpretera języka Python? Czy inne języki, które są kompilowane do kodu bajtowego, wykorzystują podobny mechanizm?

Federico A. Ramponi
źródło
6
Powinieneś także zapytać „Czy to w ogóle ma znaczenie?”
S.Lott
2
Zgadzam się, uważam to za nie problem teraz, gdy w 2.6 dodano moduł wieloprocesorowy, aby umożliwić programowanie przy użyciu wielu procesów w sposób podobny do wątków. docs.python.org/library/multiprocessing.html
monkut
Co to jest Gil: stackoverflow.com/questions/1294382/… Powiązane z programistami: softwareengineering.stackexchange.com/questions/186889/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

69

Ogólnie rzecz biorąc, w przypadku każdego problemu związanego z bezpieczeństwem wątków konieczne będzie zabezpieczenie wewnętrznych struktur danych za pomocą blokad. Można to zrobić na różnych poziomach szczegółowości.

  • Możesz użyć blokowania drobnoziarnistego, w którym każda oddzielna konstrukcja ma swój własny zamek.

  • Możesz użyć blokowania gruboziarnistego, w którym jeden zamek chroni wszystko (podejście GIL).

Każda metoda ma różne zalety i wady. Drobnoziarniste blokowanie umożliwia większą równoległość - dwa wątki mogą być wykonywane równolegle, gdy nie współużytkują żadnych zasobów. Istnieje jednak dużo większe obciążenie administracyjne. Dla każdego wiersza kodu może być konieczne nabycie i zwolnienie kilku blokad.

Podejście gruboziarniste jest odwrotne. Dwa wątki nie mogą działać w tym samym czasie, ale pojedynczy wątek będzie działał szybciej, ponieważ nie zajmuje się tak dużą księgowością. Ostatecznie sprowadza się to do kompromisu między szybkością jednowątkową a równoległością.

Było kilka prób usunięcia GIL w Pythonie, ale dodatkowe obciążenie dla maszyn jednowątkowych było generalnie zbyt duże. Niektóre przypadki mogą być faktycznie wolniejsze nawet na maszynach wieloprocesorowych z powodu rywalizacji o blokady.

Czy inne języki, które są kompilowane do kodu bajtowego, wykorzystują podobny mechanizm?

Różni się i prawdopodobnie nie powinno być uważane za właściwość języka, a raczej za właściwość implementacji. Na przykład istnieją implementacje Pythona, takie jak Jython i IronPython, które wykorzystują podejście wątkowe ich bazowej maszyny wirtualnej zamiast podejścia GIL. Dodatkowo wygląda na to, że następna wersja Rubiego zmierza w kierunku wprowadzenia GIL.

Brian
źródło
1
Czy możesz to wyjaśnić: „Dwa wątki nie mogą działać w tym samym czasie”? Niedawno napisałem prosty serwer WWW w Pythonie z wielowątkowością. Przy każdym nowym żądaniu od klienta serwery odradzają nowy wątek i ten wątek kontynuuje wykonywanie. Więc jednocześnie będzie uruchomionych wiele wątków, prawda? A może źle zrozumiałem?
avi
1
@avi AFAIK Wątki pythona nie mogą działać jednocześnie, ale to nie znaczy, że jeden wątek musi blokować drugi. GIL oznacza tylko, że tylko jeden wątek może interpretować kod Pythona na raz, nie oznacza to, że zarządzanie wątkami i alokacja zasobów nie działają.
Benproductions 1
2
^ więc w dowolnym momencie tylko jeden wątek będzie dostarczał zawartość klientowi ... więc nie ma sensu używać wielowątkowości do poprawy wydajności. dobrze?
avi
Oczywiście Java jest skompilowana do kodu bajtowego i umożliwia bardzo precyzyjne blokowanie.
Warren Dew,
3
@avi, proces powiązany z IO, taki jak serwer WWW, może nadal korzystać z wątków Pythona. Dwa lub więcej wątków może jednocześnie wykonywać operacje we / wy. Po prostu nie można ich jednocześnie interpretować (CPU).
Saish
33

Poniższy tekst pochodzi z oficjalnej instrukcji obsługi interfejsu API języka Python / C :

Interpreter Pythona nie jest w pełni bezpieczny dla wątków. Aby obsługiwać wielowątkowe programy w Pythonie, istnieje globalna blokada, która musi być utrzymywana przez bieżący wątek, zanim będzie mógł bezpiecznie uzyskać dostęp do obiektów Pythona. Bez blokady nawet najprostsze operacje mogłyby powodować problemy w programie wielowątkowym: na przykład, gdy dwa wątki jednocześnie zwiększają liczbę odwołań do tego samego obiektu, liczba odwołań mogłaby zostać zwiększona tylko raz zamiast dwukrotnie.

Dlatego istnieje reguła, że ​​tylko wątek, który uzyskał globalną blokadę interpretera, może działać na obiektach Pythona lub wywoływać funkcje API Python / C. Aby obsługiwać wielowątkowe programy w Pythonie, interpreter regularnie zwalnia i ponownie przejmuje blokadę - domyślnie co 100 instrukcji kodu bajtowego (można to zmienić za pomocą sys.setcheckinterval ()). Blokada jest również zwalniana i ponownie uzyskiwana wokół potencjalnie blokujących operacji we / wy, takich jak odczyt lub zapis pliku, dzięki czemu inne wątki mogą działać, gdy wątek żądający operacji we / wy oczekuje na zakończenie operacji we / wy.

Myślę, że to całkiem dobrze podsumowuje problem.

Eli Bendersky
źródło
1
Też to przeczytałem, ale nie rozumiem, dlaczego Python różni się pod tym względem od, powiedzmy, javy (czy to jest?)
Federico A. Ramponi
Wątki @EliBendersky Python są implementowane jako pthreads i są obsługiwane przez system operacyjny ( dabeaz.com/python/UnderstandingGIL.pdf ), podczas gdy wątki Java są wątkami na poziomie aplikacji, których planowanie jest obsługiwane przez
maszynę
19

Globalna blokada interpretera to duża blokada typu mutex, która chroni liczniki referencyjne przed podłączeniem. Jeśli piszesz czysty kod Pythona, to wszystko dzieje się za kulisami, ale jeśli osadzasz Python w C, być może będziesz musiał jawnie wziąć / zwolnić blokadę.

Ten mechanizm nie jest związany z kompilacją Pythona do kodu bajtowego. Nie jest to potrzebne w Javie. W rzeczywistości nie jest to nawet potrzebne dla Jythona (python skompilowany do jvm).

zobacz także to pytanie

David Nehme
źródło
4
„Ten mechanizm nie jest powiązany z kompilacją języka Python do kodu bajtowego”: Dokładniej, jest to artefakt implementacji CPythona. Inne implementacje (takie jak Jython, o którym wspomniałeś) mogą być wolne od tego ograniczenia ze względu na ich bezpieczną
wątkowo
11

Python, podobnie jak Perl 5, nie został zaprojektowany od podstaw, aby był bezpieczny dla wątków. Wątki zostały zaszczepione po fakcie, więc globalna blokada interpretera służy do utrzymania wzajemnego wykluczenia, gdy tylko jeden wątek wykonuje kod w danym momencie w trzewiach interpretera.

Poszczególne wątki Pythona są wspólnie wielozadaniowe przez samego tłumacza poprzez cykliczne włączanie blokady co jakiś czas.

Samodzielne chwycenie blokady jest potrzebne, gdy rozmawiasz z Pythonem z C, gdy inne wątki Pythona są aktywne, aby „włączyć się” do tego protokołu i upewnić się, że za Twoimi plecami nie dzieje się nic niebezpiecznego.

Inne systemy, które mają jednowątkowe dziedzictwo, które później przekształciły się w systemy wielowątkowe, często mają taki mechanizm. Na przykład jądro Linuksa ma „Big Kernel Lock” od wczesnych dni SMP. Stopniowo w miarę upływu czasu, gdy wydajność wielowątkowości staje się problemem, istnieje tendencja do podejmowania prób rozbijania tego rodzaju blokad na mniejsze części lub zastępowania ich tam, gdzie to możliwe, algorytmami i strukturami danych bez blokad, aby zmaksymalizować przepustowość.

Edward KMETT
źródło
+1 za wzmiankę o tym, że stosuje się gruboziarniste blokowanie, niż myśli większość, zwłaszcza często zapomniany BKL (używam reiserfs- jedyny prawdziwy powód, dla którego w ogóle o tym wiem).
nowy123456
3
Linux miał BKL, od wersji 2.6.39 BKL został całkowicie usunięty.
avi
5
Oczywiście. Pamiętaj, że to było ~ 3 lata po tym, jak odpowiedziałem na pytanie. =)
Edward KMETT
7

Jeśli chodzi o drugie pytanie, nie wszystkie języki skryptowe tego używają, ale tylko zmniejsza ich możliwości. Na przykład wątki w Rubim są zielone, a nie natywne.

W Pythonie wątki są natywne, a GIL zapobiega tylko ich działaniu na różnych rdzeniach.

W Perlu wątki są jeszcze gorsze. Po prostu kopiują cały interpreter i nie są tak użyteczne jak w Pythonie.

Eli Bendersky
źródło
2

Może ten artykuł BDFL pomoże.

Jeremy Cantrell
źródło