🕷️ Crawler Inspector

URL Lookup

Direct Parameter Lookup

Raw Queries and Responses

1. Shard Calculation

Query:
Response:
Calculated Shard: 84 (from laksa036)

2. Crawled Status Check

Query:
Response:

3. Robots.txt Check

Query:
Response:

4. Spam/Ban Check

Query:
Response:

5. Seen Status Check

ℹ️ Skipped - page is already crawled

📄
INDEXABLE
CRAWLED
1 month ago
🤖
ROBOTS ALLOWED

Page Info Filters

FilterStatusConditionDetails
HTTP statusPASSdownload_http_code = 200HTTP 200
Age cutoffPASSdownload_stamp > now() - 6 MONTH1.2 months ago
History dropPASSisNull(history_drop_reason)No drop reason
Spam/banPASSfh_dont_index != 1 AND ml_spam_score = 0ml_spam_score=0
CanonicalPASSmeta_canonical IS NULL OR = '' OR = src_unparsedNot set

Page Details

PropertyValue
URLhttps://senjun.ru/courses/python/chapters/python_chapter_0290/
Last Crawled2026-03-04 06:03:54 (1 month ago)
First Indexed2024-01-12 17:27:26 (2 years ago)
HTTP Status Code200
Meta TitleGIL | Python. Глава 29
Meta DescriptionКто-то называет GIL (global interpreter lock) главной архитектурной ошибкой питона. Кто-то — неизбежным компромиссом между скоростью выполнения однопоточных и многопоточных скриптов. А кто-то — вполне удачным решением, от которого бессмысленно отказываться. Разберем, что такое GIL, откуда у него растут ноги, какие на него планы у мейнтейнеров языка и что с этим делать простым разработчикам.
Meta Canonicalnull
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) — разработчика, внесшего большой вклад в развитие питона и сообщества вокруг него. ![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 лучше подойдет пул процессов. Прикрепить файлы Отправка...
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/) Если вам нравится курс, вы можете поддержать развитие площадки\! [![SenJun Telegram Bot](https://senjun.ru/static/courses/v55/images/bot.svg)](https://t.me/sen_jun_bot) [Бот для прохождения курсов](https://t.me/sen_jun_bot) [![SenJun Community Group](https://senjun.ru/static/courses/v55/images/telegram.svg)](https://t.me/senjun_feedback) [Наше сообщество](https://t.me/senjun_feedback) [![SenJun on GitHub](https://senjun.ru/static/courses/v55/images/github.svg)](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 лучше подойдет пул процессов.
Shard84 (laksa)
Root Hash5944480186913663084
Unparsed URLru,senjun!/courses/python/chapters/python_chapter_0290/ s443