ℹ️ Skipped - page is already crawled
| Filter | Status | Condition | Details |
|---|---|---|---|
| HTTP status | PASS | download_http_code = 200 | HTTP 200 |
| Age cutoff | PASS | download_stamp > now() - 6 MONTH | 1.2 months ago |
| History drop | PASS | isNull(history_drop_reason) | No drop reason |
| Spam/ban | PASS | fh_dont_index != 1 AND ml_spam_score = 0 | ml_spam_score=0 |
| Canonical | PASS | meta_canonical IS NULL OR = '' OR = src_unparsed | Not set |
| Property | Value |
|---|---|
| URL | https://senjun.ru/courses/python/chapters/python_chapter_0290/ |
| Last Crawled | 2026-03-04 06:03:54 (1 month ago) |
| First Indexed | 2024-01-12 17:27:26 (2 years ago) |
| HTTP Status Code | 200 |
| Meta Title | GIL | Python. Глава 29 |
| Meta Description | Кто-то называет GIL (global interpreter lock) главной архитектурной ошибкой питона. Кто-то — неизбежным компромиссом между скоростью выполнения однопоточных и многопоточных скриптов. А кто-то — вполне удачным решением, от которого бессмысленно отказываться. Разберем, что такое GIL, откуда у него растут ноги, какие на него планы у мейнтейнеров языка и что с этим делать простым разработчикам. |
| Meta Canonical | null |
| Boilerpipe Text | # Глава 29. GIL
Кто-то называет GIL (global interpreter lock) главной архитектурной ошибкой питона. Кто-то — неизбежным компромиссом между скоростью выполнения однопоточных и многопоточных скриптов. А кто-то — вполне удачным решением, от которого бессмысленно отказываться.
Разберем, что такое GIL, откуда у него растут ноги, какие на него планы у мейнтейнеров языка и что с этим делать простым разработчикам.
## Что такое GIL
GIL, то есть глобальная блокировка интерпретатора, гарантирует, что в каждый момент времени байт-код скрипта исполняется только одним потоком ОС. Из-за GIL питон лишен истинной параллельности выполнения разных потоков. Даже на многоядерных CPU. При этом GIL не препятствует параллельности выполнения разных процессов: на каждый процесс скрипта запускается отдельный процесс интерпретатора со своим GIL.
Для чего нужен GIL? При выполнении кода интерпретатор работает с потоко-небезопасными переменными. Чтобы гарантировать их сохранность, каждый поток интерпретатора должен захватывать и отпускать GIL. К таким переменным, например, относится счетчик ссылок.
Кстати, значение счетчика ссылок можно посмотреть для каждого объекта.
```python {.example_for_playground}
from sys import getrefcount
d = {}
d2 = d
print(getrefcount(d))
```
```
3
```
В примере у пустого словаря количество ссылок равно 3: на него ссылаются переменные `d` и `d2`, но откуда третья ссылка? Возвращаемое `getrefcount()` количество как правило на единицу больше, чем ожидается. Оно учитывает временную ссылку в качестве аргумента самой функции `getrefcount()`.
Закономерно возникает вопрос: нельзя ли вместо блокирования потока целиком блокировать каждую потоко-небезопасную переменную отдельно? Синхронизация доступа к отдельным объектам вместо глобальной блокировки приводит к частым захватам/освобождениям этих самых блокировок. А это примерно [на 30%](https://docs.python.org/3/faq/library.html#can-t-we-get-rid-of-the-global-interpreter-lock) замедляет однопоточные скрипты. Поэтому разработчики языка сделали выбор в пользу глобальной блокировки.
Пара важных фактов о GIL:
- GIL не является частью языка. Это особенность реализации интерпретатора. Он есть в стандартном интерпретаторе CPython, написанном на C. Также он есть в [PyPy,](https://www.pypy.org/) написанном на языке RPython. Но GIL отсутствует в таких интерпретаторах как [Jython](https://www.jython.org/) (написан на Java) и [IronPython](https://ironpython.net/) (написан на C#). Там все проблемы синхронизации делегируются виртуальным машинам JVM и .NET/Mono.
- GIL встречается в реализациях других интерпретируемых языков. Например, стандартная имплементация [Ruby](https://www.ruby-lang.org/en/) под названием Ruby MRI содержит GIL. Только называется он Global VM Lock.
## Следствия наличия GIL
Как же правильно распараллеливать код, исполняющийся интерпретатором с GIL? Это зависит от специфики распараллеливаемых задач. {#block-cpu-bound}
**CPU-bound** задачи — это вычисления, грузящие процессор: полнотекстовый поиск, обход графа, перемножение матриц и так далее. При использовании GIL-интерпретатора получить выигрыш в производительности за счет потоков не получится. Оверхед на переключение контекста между потоками в связке с захватом и разблокировкой GIL могут сделать многопоточный код даже медленнее его однопоточной версии. Если требуется распараллелить CPU-bound работу, вместо потоков используйте процессы.
**IO-bound** задачи, такие как обращение к внешнему API, работа с бд, файлами и консольным вводом-выводом, массу времени проводят в режиме ожидания данных. А блокирующее ожидание ввода-вывода заставляет поток отпустить GIL. Поэтому распараллеливание IO-bound на потоки все же может принести выигрыш в скорости. Для достижения наилучших результатов количество потоков подбирается в зависимости от конфигурации целевой машины и специфики конкретной IO-bound задачи.
Примерно так выглядит выполнение 2-х CPU-bound потоков на машине с единственным CPU. Иллюстрация взята из блога [Дэвида Бизли](https://dabeaz.blogspot.com/2010/01/python-gil-visualized.html) — разработчика, внесшего большой вклад в развитие питона и сообщества вокруг него.
 {.illustration}
 {.illustration}
А теперь запустим 2 CPU-bound потока на машине с двумя CPU.
 {.illustration}
Иллюстрация демонстрирует, насколько неэффективно запускать несколько потоков питона в попытках распараллелить CPU-bound задачи. В интервалы времени, окрашенные красным, ОС передавала управление потоку, который пытался захватить GIL и ждал, пока его освободит другой поток.
Подытожим: для распараллеливания CPU-bound задач подойдет мультипроцессность, а для IO-bound задач — многопоточность.
## GIL и будущее питона
На данный момент развиваются два глобальных направления разработки CPython. И оба затрагивают GIL.
Полноценная поддержка **subinterpreters.** О чем речь? Начиная с версии 1.5 (то есть с 1997 года) разработчики на питоне могли воспользоваться C-API для запуска нескольких экземпляров интерпретатора в рамках одного процесса. Эти интерпретаторы разделяли состояние: таблицу имен в глобальной области видимости, кэш импортированных модулей и т.д. Соответственно GIL тоже был общим.
В версии языка 3.12 удалось добиться изоляции интерпретаторов внутри процесса: теперь у каждого из них свое состояние и свой собственный GIL. Subinterpreters по-прежнему доступны только через C-API. А полноценная поддержка планируется в версии 3.13.
Проект **nogil:** полный отказ от GIL. Исследователь-разработчик [Сэм Гросс](https://mail.python.org/archives/list/python-dev@python.org/thread/ABR2L6BENNA6UPSPKV474HCS4LWT26GY/) предложил реализацию CPython без GIL под названием [nogil.](https://github.com/colesbury/nogil) В ней удалось добиться удаления GIL таким образом, чтобы однопоточный код не потерял в производительности, а многопоточный хорошо масштабировался на ядра CPU.
Уже принят [PEP 703,](https://peps.python.org/pep-0703/) в рамках которого ведутся работы, направленные на внесение всех нужных изменений в CPython.
## Резюмируем
- GIL (global interpreter lock) — блокировка интерпретатора, которая гарантирует, что в каждый момент времени интерпретатор исполняет только один поток скрипта на питоне.
- GIL не позволяет эффективно использовать многоядерность путем распараллеливания на потоки.
- Есть два направления развития языка, связанных с GIL: отказ от GIL (nogil) и изоляция GIL для каждого интерпретатора внутри потока (subinterpreters).
- Для ускорения выполнения IO-bound задач можно использовать пул потоков. Для ускорения CPU-bound лучше подойдет пул процессов.
Прикрепить файлы
Отправка... |
| Markdown | ×
Содержание главы
[SenJun](https://senjun.ru/ "Senior Junior")
[Курсы](https://senjun.ru/courses)
[Песочница](https://senjun.ru/playground)
Вход
[Логин](https://senjun.ru/login/) [Регистрация](https://senjun.ru/signup/)
[Главная](https://senjun.ru/) / [Курсы](https://senjun.ru/courses/) / [Python](https://senjun.ru/courses/python) / GIL
\# Глава 29. GIL Кто-то называет GIL (global interpreter lock) главной архитектурной ошибкой питона. Кто-то — неизбежным компромиссом между скоростью выполнения однопоточных и многопоточных скриптов. А кто-то — вполне удачным решением, от которого бессмысленно отказываться. Разберем, что такое GIL, откуда у него растут ноги, какие на него планы у мейнтейнеров языка и что с этим делать простым разработчикам. \## Что такое GIL GIL, то есть глобальная блокировка интерпретатора, гарантирует, что в каждый момент времени байт-код скрипта исполняется только одним потоком ОС. Из-за GIL питон лишен истинной параллельности выполнения разных потоков. Даже на многоядерных CPU. При этом GIL не препятствует параллельности выполнения разных процессов: на каждый процесс скрипта запускается отдельный процесс интерпретатора со своим GIL. Для чего нужен GIL? При выполнении кода интерпретатор работает с потоко-небезопасными переменными. Чтобы гарантировать их сохранность, каждый поток интерпретатора должен захватывать и отпускать GIL. К таким переменным, например, относится счетчик ссылок. Кстати, значение счетчика ссылок можно посмотреть для каждого объекта. \```python {.example\_for\_playground} from sys import getrefcount d = {} d2 = d print(getrefcount(d)) \``` \``` 3 \``` В примере у пустого словаря количество ссылок равно 3: на него ссылаются переменные \`d\` и \`d2\`, но откуда третья ссылка? Возвращаемое \`getrefcount()\` количество как правило на единицу больше, чем ожидается. Оно учитывает временную ссылку в качестве аргумента самой функции \`getrefcount()\`. Закономерно возникает вопрос: нельзя ли вместо блокирования потока целиком блокировать каждую потоко-небезопасную переменную отдельно? Синхронизация доступа к отдельным объектам вместо глобальной блокировки приводит к частым захватам/освобождениям этих самых блокировок. А это примерно \[на 30%\](https://docs.python.org/3/faq/library.html\#can-t-we-get-rid-of-the-global-interpreter-lock) замедляет однопоточные скрипты. Поэтому разработчики языка сделали выбор в пользу глобальной блокировки. Пара важных фактов о GIL: - GIL не является частью языка. Это особенность реализации интерпретатора. Он есть в стандартном интерпретаторе CPython, написанном на C. Также он есть в \[PyPy,\](https://www.pypy.org/) написанном на языке RPython. Но GIL отсутствует в таких интерпретаторах как \[Jython\](https://www.jython.org/) (написан на Java) и \[IronPython\](https://ironpython.net/) (написан на C\#). Там все проблемы синхронизации делегируются виртуальным машинам JVM и .NET/Mono. - GIL встречается в реализациях других интерпретируемых языков. Например, стандартная имплементация \[Ruby\](https://www.ruby-lang.org/en/) под названием Ruby MRI содержит GIL. Только называется он Global VM Lock. \## Следствия наличия GIL Как же правильно распараллеливать код, исполняющийся интерпретатором с GIL? Это зависит от специфики распараллеливаемых задач. {\#block-cpu-bound} \*\*CPU-bound\*\* задачи — это вычисления, грузящие процессор: полнотекстовый поиск, обход графа, перемножение матриц и так далее. При использовании GIL-интерпретатора получить выигрыш в производительности за счет потоков не получится. Оверхед на переключение контекста между потоками в связке с захватом и разблокировкой GIL могут сделать многопоточный код даже медленнее его однопоточной версии. Если требуется распараллелить CPU-bound работу, вместо потоков используйте процессы. \*\*IO-bound\*\* задачи, такие как обращение к внешнему API, работа с бд, файлами и консольным вводом-выводом, массу времени проводят в режиме ожидания данных. А блокирующее ожидание ввода-вывода заставляет поток отпустить GIL. Поэтому распараллеливание IO-bound на потоки все же может принести выигрыш в скорости. Для достижения наилучших результатов количество потоков подбирается в зависимости от конфигурации целевой машины и специфики конкретной IO-bound задачи. Примерно так выглядит выполнение 2-х CPU-bound потоков на машине с единственным CPU. Иллюстрация взята из блога \[Дэвида Бизли\](https://dabeaz.blogspot.com/2010/01/python-gil-visualized.html) — разработчика, внесшего большой вклад в развитие питона и сообщества вокруг него. !\[2 CPU-bound потока на машине с 1 CPU.\](https://raw.githubusercontent.com/senjun-team/senjun-courses/main/illustrations/python/2-threads-1-cpu.png) {.illustration} !\[Легенда.\](https://raw.githubusercontent.com/senjun-team/senjun-courses/main/illustrations/python/theads-cpu-legend.png) {.illustration} А теперь запустим 2 CPU-bound потока на машине с двумя CPU. !\[2 CPU-bound потока на машине с 2 CPU.\](https://raw.githubusercontent.com/senjun-team/senjun-courses/main/illustrations/python/2-threads-2-cpu.png) {.illustration} Иллюстрация демонстрирует, насколько неэффективно запускать несколько потоков питона в попытках распараллелить CPU-bound задачи. В интервалы времени, окрашенные красным, ОС передавала управление потоку, который пытался захватить GIL и ждал, пока его освободит другой поток. Подытожим: для распараллеливания CPU-bound задач подойдет мультипроцессность, а для IO-bound задач — многопоточность. \## GIL и будущее питона На данный момент развиваются два глобальных направления разработки CPython. И оба затрагивают GIL. Полноценная поддержка \*\*subinterpreters.\*\* О чем речь? Начиная с версии 1.5 (то есть с 1997 года) разработчики на питоне могли воспользоваться C-API для запуска нескольких экземпляров интерпретатора в рамках одного процесса. Эти интерпретаторы разделяли состояние: таблицу имен в глобальной области видимости, кэш импортированных модулей и т.д. Соответственно GIL тоже был общим. В версии языка 3.12 удалось добиться изоляции интерпретаторов внутри процесса: теперь у каждого из них свое состояние и свой собственный GIL. Subinterpreters по-прежнему доступны только через C-API. А полноценная поддержка планируется в версии 3.13. Проект \*\*nogil:\*\* полный отказ от GIL. Исследователь-разработчик \[Сэм Гросс\](https://mail.python.org/archives/list/python-dev@python.org/thread/ABR2L6BENNA6UPSPKV474HCS4LWT26GY/) предложил реализацию CPython без GIL под названием \[nogil.\](https://github.com/colesbury/nogil) В ней удалось добиться удаления GIL таким образом, чтобы однопоточный код не потерял в производительности, а многопоточный хорошо масштабировался на ядра CPU. Уже принят \[PEP 703,\](https://peps.python.org/pep-0703/) в рамках которого ведутся работы, направленные на внесение всех нужных изменений в CPython. \## Резюмируем - GIL (global interpreter lock) — блокировка интерпретатора, которая гарантирует, что в каждый момент времени интерпретатор исполняет только один поток скрипта на питоне. - GIL не позволяет эффективно использовать многоядерность путем распараллеливания на потоки. - Есть два направления развития языка, связанных с GIL: отказ от GIL (nogil) и изоляция GIL для каждого интерпретатора внутри потока (subinterpreters). - Для ускорения выполнения IO-bound задач можно использовать пул потоков. Для ускорения CPU-bound лучше подойдет пул процессов.
[Дальше](https://senjun.ru/courses/python/chapters/python_chapter_0300) [Поддержать проект](https://senjun.ru/donation/)
[Наша группа в telegram.](https://t.me/senjun_feedback) Здесь можно задавать вопросы и общаться.
[Задонатить.](https://senjun.ru/donation/) Если вам нравится курс, вы можете поддержать развитие площадки\!
[](https://t.me/sen_jun_bot)
[Бот для прохождения курсов](https://t.me/sen_jun_bot)
[](https://t.me/senjun_feedback)
[Наше сообщество](https://t.me/senjun_feedback)
[](https://github.com/senjun-team/senjun-courses)
[Проект на GitHub](https://github.com/senjun-team/senjun-courses)
[team@senjun.ru](<mailto: team@senjun.ru>)
[Поддержать проект](https://senjun.ru/donation/)
© 2024, SenJun
[Условия использования](https://senjun.ru/terms_of_use/) |
| Readable Markdown | \# Глава 29. GIL Кто-то называет GIL (global interpreter lock) главной архитектурной ошибкой питона. Кто-то — неизбежным компромиссом между скоростью выполнения однопоточных и многопоточных скриптов. А кто-то — вполне удачным решением, от которого бессмысленно отказываться. Разберем, что такое GIL, откуда у него растут ноги, какие на него планы у мейнтейнеров языка и что с этим делать простым разработчикам. \## Что такое GIL GIL, то есть глобальная блокировка интерпретатора, гарантирует, что в каждый момент времени байт-код скрипта исполняется только одним потоком ОС. Из-за GIL питон лишен истинной параллельности выполнения разных потоков. Даже на многоядерных CPU. При этом GIL не препятствует параллельности выполнения разных процессов: на каждый процесс скрипта запускается отдельный процесс интерпретатора со своим GIL. Для чего нужен GIL? При выполнении кода интерпретатор работает с потоко-небезопасными переменными. Чтобы гарантировать их сохранность, каждый поток интерпретатора должен захватывать и отпускать GIL. К таким переменным, например, относится счетчик ссылок. Кстати, значение счетчика ссылок можно посмотреть для каждого объекта. \```python {.example\_for\_playground} from sys import getrefcount d = {} d2 = d print(getrefcount(d)) \``` \``` 3 \``` В примере у пустого словаря количество ссылок равно 3: на него ссылаются переменные \`d\` и \`d2\`, но откуда третья ссылка? Возвращаемое \`getrefcount()\` количество как правило на единицу больше, чем ожидается. Оно учитывает временную ссылку в качестве аргумента самой функции \`getrefcount()\`. Закономерно возникает вопрос: нельзя ли вместо блокирования потока целиком блокировать каждую потоко-небезопасную переменную отдельно? Синхронизация доступа к отдельным объектам вместо глобальной блокировки приводит к частым захватам/освобождениям этих самых блокировок. А это примерно \[на 30%\](https://docs.python.org/3/faq/library.html\#can-t-we-get-rid-of-the-global-interpreter-lock) замедляет однопоточные скрипты. Поэтому разработчики языка сделали выбор в пользу глобальной блокировки. Пара важных фактов о GIL: - GIL не является частью языка. Это особенность реализации интерпретатора. Он есть в стандартном интерпретаторе CPython, написанном на C. Также он есть в \[PyPy,\](https://www.pypy.org/) написанном на языке RPython. Но GIL отсутствует в таких интерпретаторах как \[Jython\](https://www.jython.org/) (написан на Java) и \[IronPython\](https://ironpython.net/) (написан на C\#). Там все проблемы синхронизации делегируются виртуальным машинам JVM и .NET/Mono. - GIL встречается в реализациях других интерпретируемых языков. Например, стандартная имплементация \[Ruby\](https://www.ruby-lang.org/en/) под названием Ruby MRI содержит GIL. Только называется он Global VM Lock. \## Следствия наличия GIL Как же правильно распараллеливать код, исполняющийся интерпретатором с GIL? Это зависит от специфики распараллеливаемых задач. {\#block-cpu-bound} \*\*CPU-bound\*\* задачи — это вычисления, грузящие процессор: полнотекстовый поиск, обход графа, перемножение матриц и так далее. При использовании GIL-интерпретатора получить выигрыш в производительности за счет потоков не получится. Оверхед на переключение контекста между потоками в связке с захватом и разблокировкой GIL могут сделать многопоточный код даже медленнее его однопоточной версии. Если требуется распараллелить CPU-bound работу, вместо потоков используйте процессы. \*\*IO-bound\*\* задачи, такие как обращение к внешнему API, работа с бд, файлами и консольным вводом-выводом, массу времени проводят в режиме ожидания данных. А блокирующее ожидание ввода-вывода заставляет поток отпустить GIL. Поэтому распараллеливание IO-bound на потоки все же может принести выигрыш в скорости. Для достижения наилучших результатов количество потоков подбирается в зависимости от конфигурации целевой машины и специфики конкретной IO-bound задачи. Примерно так выглядит выполнение 2-х CPU-bound потоков на машине с единственным CPU. Иллюстрация взята из блога \[Дэвида Бизли\](https://dabeaz.blogspot.com/2010/01/python-gil-visualized.html) — разработчика, внесшего большой вклад в развитие питона и сообщества вокруг него. !\[2 CPU-bound потока на машине с 1 CPU.\](https://raw.githubusercontent.com/senjun-team/senjun-courses/main/illustrations/python/2-threads-1-cpu.png) {.illustration} !\[Легенда.\](https://raw.githubusercontent.com/senjun-team/senjun-courses/main/illustrations/python/theads-cpu-legend.png) {.illustration} А теперь запустим 2 CPU-bound потока на машине с двумя CPU. !\[2 CPU-bound потока на машине с 2 CPU.\](https://raw.githubusercontent.com/senjun-team/senjun-courses/main/illustrations/python/2-threads-2-cpu.png) {.illustration} Иллюстрация демонстрирует, насколько неэффективно запускать несколько потоков питона в попытках распараллелить CPU-bound задачи. В интервалы времени, окрашенные красным, ОС передавала управление потоку, который пытался захватить GIL и ждал, пока его освободит другой поток. Подытожим: для распараллеливания CPU-bound задач подойдет мультипроцессность, а для IO-bound задач — многопоточность. \## GIL и будущее питона На данный момент развиваются два глобальных направления разработки CPython. И оба затрагивают GIL. Полноценная поддержка \*\*subinterpreters.\*\* О чем речь? Начиная с версии 1.5 (то есть с 1997 года) разработчики на питоне могли воспользоваться C-API для запуска нескольких экземпляров интерпретатора в рамках одного процесса. Эти интерпретаторы разделяли состояние: таблицу имен в глобальной области видимости, кэш импортированных модулей и т.д. Соответственно GIL тоже был общим. В версии языка 3.12 удалось добиться изоляции интерпретаторов внутри процесса: теперь у каждого из них свое состояние и свой собственный GIL. Subinterpreters по-прежнему доступны только через C-API. А полноценная поддержка планируется в версии 3.13. Проект \*\*nogil:\*\* полный отказ от GIL. Исследователь-разработчик \[Сэм Гросс\](https://mail.python.org/archives/list/python-dev@python.org/thread/ABR2L6BENNA6UPSPKV474HCS4LWT26GY/) предложил реализацию CPython без GIL под названием \[nogil.\](https://github.com/colesbury/nogil) В ней удалось добиться удаления GIL таким образом, чтобы однопоточный код не потерял в производительности, а многопоточный хорошо масштабировался на ядра CPU. Уже принят \[PEP 703,\](https://peps.python.org/pep-0703/) в рамках которого ведутся работы, направленные на внесение всех нужных изменений в CPython. \## Резюмируем - GIL (global interpreter lock) — блокировка интерпретатора, которая гарантирует, что в каждый момент времени интерпретатор исполняет только один поток скрипта на питоне. - GIL не позволяет эффективно использовать многоядерность путем распараллеливания на потоки. - Есть два направления развития языка, связанных с GIL: отказ от GIL (nogil) и изоляция GIL для каждого интерпретатора внутри потока (subinterpreters). - Для ускорения выполнения IO-bound задач можно использовать пул потоков. Для ускорения CPU-bound лучше подойдет пул процессов. |
| Shard | 84 (laksa) |
| Root Hash | 5944480186913663084 |
| Unparsed URL | ru,senjun!/courses/python/chapters/python_chapter_0290/ s443 |