Чому Глобальний перекладач?


89

Яка саме функція блокування інтерпретатора Python? Чи використовують інші мови, скомпільовані в байт-код, подібний механізм?


6
Ви також повинні запитати "Чи це взагалі має значення?"
S.Lott

2
Погоджуюсь, я вважаю непростою проблему зараз, коли в 2.6 було додано модуль багатопроцесорної обробки, що дозволяє вам програмувати, використовуючи безліч процесів, у вигляді потоку. docs.python.org/library/multiprocessing.html
monkut

Відповіді:


69

Загалом, для будь-якої проблеми безпеки потоку вам потрібно буде захистити свої внутрішні структури даних замками. Це можна зробити з різним рівнем деталізації.

  • Ви можете використовувати дрібнозернистий замок, де кожна окрема конструкція має власний замок.

  • Ви можете використовувати грубозернистий замок, де один замок захищає все (підхід GIL).

У кожного методу є різні плюси і мінуси. Дрібнозернисте блокування забезпечує більший паралелізм - два потоки можуть виконуватися паралельно, коли вони не мають спільних ресурсів. Однак є значно більші адміністративні накладні витрати. Для кожного рядка коду вам може знадобитися придбати та звільнити кілька блокувань.

Грубозернистий підхід протилежний. Два потоки не можуть працювати одночасно, але окремий потік буде працювати швидше, оскільки він не робить так багато бухгалтерії. Зрештою, це зводиться до компромісу між однопоточною швидкістю та паралелізмом.

Було кілька спроб видалити GIL у python, але додаткові накладні витрати для однопоточних машин, як правило, були занадто великими. Деякі випадки можуть бути повільнішими навіть на багатопроцесорних машинах через суперечку про блокування.

Чи використовують інші мови, скомпільовані в байт-код, подібний механізм?

Вона варіюється, і, ймовірно, її не слід вважати властивістю мови настільки, як властивістю реалізації. Наприклад, існують реалізації Python, такі як Jython та IronPython, які використовують підхід з потоками своєї базової ВМ, а не підхід GIL. Крім того, наступна версія Ruby, схоже, рухається до запровадження GIL.


1
Ви можете пояснити це: "Два потоки не можуть працювати одночасно"? Нещодавно я написав простий веб-сервер на Python з багатопоточністю. Для кожного нового запиту від клієнта сервери створюють для нього новий потік, і цей потік продовжує виконуватися. Отже, буде виконуватися кілька потоків одночасно, чи не так? Або я зрозумів не так?
avi

1
Потоки пітона @avi AFAIK не можуть працювати одночасно, але це не означає, що один потік повинен блокувати інший. GIL лише означає, що лише один потік може інтерпретувати код python одночасно, це не означає, що управління потоками та розподіл ресурсів не працює.
Benproductions1

2
^ отже, у будь-який момент часу лише один потік буде обслуговувати вміст для клієнта ... так що немає сенсу фактично використовувати багатопоточність для підвищення продуктивності. так?
avi

І, звичайно, Java компілюється в байтовий код і дозволяє дуже тонко розмежувати блокування.
Уоррен Дью,

3
@avi, процес, пов'язаний з IO, як веб-сервер, все ще може отримати від потоків Python. Два або більше потоків можуть виконувати введення-виведення одночасно. Їх просто не можна інтерпретувати (ЦП) одночасно.
Саїш

33

Далі йдеться з офіційного довідкового посібника API Python / C :

Інтерпретатор Python не є повністю безпечним для потоків. Для підтримки багатопотокових програм Python існує глобальний замок, який повинен утримуватися поточним потоком, перш ніж він зможе безпечно отримати доступ до об’єктів Python. Без блокування навіть найпростіші операції можуть викликати проблеми в багатопотоковій програмі: наприклад, коли два потоки одночасно збільшують кількість посилань одного і того ж об'єкта, кількість посилань може в підсумку збільшитися лише один раз, а не двічі.

Отже, існує правило, що лише потік, який отримав глобальну блокування інтерпретатора, може працювати з об'єктами Python або викликати функції API Python / C. Для підтримки багатопотокових програм Python інтерпретатор регулярно випускає і знову отримує блокування - за замовчуванням кожні 100 інструкцій байт-коду (це можна змінити за допомогою sys.setcheckinterval ()). Блокування також вивільняється та повторно отримується навколо потенційно блокуючих операцій вводу-виводу, таких як читання або запис файлу, так що інші потоки можуть працювати, поки потік, що запитує введення-виведення, чекає завершення операції вводу-виводу.

Думаю, це досить добре підсумовує проблему.


1
Я теж його читав, але не можу зрозуміти, чому Python відрізняється в цьому відношенні від, скажімо, Java (
правда

Потоки @EliBendersky Python реалізовані як pthreads і обробляються ОС ( dabeaz.com/python/UnderstandingGIL.pdf ), тоді як потоки Java - це потоки рівня додатку, планування яких обробляється JVM
gokul_uf

19

Глобальний інтерпретатор - це великий замок типу mutex, який захищає лічильники посилань від шлангу. Якщо ви пишете чистий код python, все це відбувається за лаштунками, але якщо ви вбудовуєте Python в C, можливо, вам доведеться явно взяти / звільнити блокування.

Цей механізм не пов'язаний з Python, який компілюється в байт-код. Це не потрібно для Java. Насправді це навіть не потрібно для Jython (python скомпільований у jvm).

див. також це питання


4
"Цей механізм не пов'язаний з Python, який компілюється в байт-код": Точніше, це артефакт реалізації CPython. Інші реалізації (наприклад, Jython, про яку ви вже згадали) можуть бути вільними від цього обмеження завдяки їх поточно безпечній реалізації
Елі Бендерскі,

11

Python, як і perl 5, не був розроблений з нуля, щоб бути безпечним для потоків. Потоки були прищеплені після факту, тому глобальний блокувальник інтерпретатора використовується для підтримки взаємного виключення туди, де лише один потік виконує код у певний момент часу в надрах інтерпретатора.

Індивідуальні потоки Python багатозадачно виконуються інтерпретатором, періодично перемикаючи блокування.

Самостійно захопити замок потрібно, коли ви розмовляєте з Python з C, коли інші потоки Python активні, щоб «увімкнути» цей протокол і переконатися, що за вашою спиною не відбувається нічого небезпечного.

Інші системи, які мають однопоточну спадщину, яка пізніше переросла у багатопотокові системи, часто мають певний механізм подібного роду. Наприклад, ядро ​​Linux має "Блокування великого ядра" з перших днів SMP. Поступово, з часом, як продуктивність багатопоточності стає проблемою, існує тенденція намагатися розбити подібні замки на менші частини або замінити їх безблокувальними алгоритмами та структурами даних, де це можливо, щоб максимізувати пропускну здатність.


+1 за згадку про те, що використовується грубозернистий замок, ніж думають більшість, особливо часто забутий BKL (я використовую reiserfs- єдина справжня причина, що я про це взагалі знаю).
new123456 07

3
Linux мав BKL, оскільки з версії 2.6.39 BKL було повністю видалено.
avi

5
Звичайно. Майте на увазі, це було ~ 3 роки після того, як я відповів на запитання. =)
Едвард КМЕТТ

7

Що стосується Вашого другого питання, не всі мови сценаріїв використовують це, але це лише робить їх менш потужними. Наприклад, нитки в Ruby зелені і не є рідними.

У Python потоки є рідними, і GIL лише заважає їм працювати на різних ядрах.

У Perl нитки ще гірші. Вони просто копіюють весь інтерпретатор і далеко не настільки корисні, як у Python.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.