В своем докладе Григорий проведет краткий экскурс в историю потоков и расскажет, зачем был создан GIL. Будут рассмотрены практические вопросы многопоточности в Python и способы работы с GIL.
простые: они выполняли только одну программу за раз. Все программы загружались в общую память и передавали друг другу управление – кооперативная многозадачность.
программисты создали абстракцию - процесс. С кооперативной многозадачностью. Все программы загружались в общую память и передавали друг другу управление.
ошибок - слишком дорого. И когда программа зависала, она не передавала никому управление, и зависали все запущенные программы во главе с операционной системой. … А еще программы портили память. Надо было что-то делать.
мафия операционная система, которая обозревает спящие процессы и решает, кому из них просыпаться. Зависшие процессы больше не останавливают операционную систему. Правильно написанная программа большую часть времени спит, ожидая чего-нибудь интересного. Ее разбудят.
программа считает, что у нее есть вся память от 0 до бесконечности*. А при записи и чтении соответствующий кусочек памяти записывается и читается в физическую память. * Для 32-битных систем бесконечность - от 2 до 3 гигабайт, в зависимости от настроек.
сложных программ возник ряд вопросов: • Если программа читает файл, то она ждет, пока операционная система вернет управление. • Асинхронные функции и алгоритмы слишком сложны и не всегда доступны. • Запуск дополнительных процессов слишком долгий. * * Для Windows :)
и просыпается так же, как раньше засыпали и просыпались процессы. • Читает и пишет ту же виртуальную память, что и остальные потоки одного процесса. • Быстро стартует. • При создании процесса создается первый, он же “основной” поток.
- в одновременном доступе к памяти? Вас обманули. Компьютер ничего не делает “одновременно” - он играет в пошаговую стратегию. * • Проблема потоков в том, что они засыпают и просыпаются в неожиданные моменты времени. * Многоядерные играют в несколько непересекающихся пошаговых стратегий.
работает Поток работает Поток спит Поток спит Поток спит Общая память потоков Общая память потоков Общая память потоков Указатель не 0? Читаем по адресу Обнулить :) Указатель → адрес Указатель → 0 Указатель → 0
без GIL не выполняются одновременно, то как GIL может, цитируя документацию, “prevent multiple native threads from executing Python bytecodes at once (одновременно)”?
без GIL не выполняются одновременно, то как GIL может, цитируя документацию, “prevent multiple native threads from executing Python bytecodes at once (одновременно)”? А никак. Он не это делает.
своих потоков, но она может контролировать, когда они проснутся. Python принудительно погружает все потоки, кроме текущего, в сон. Они спят и ждут светлого будущего.
своих потоков, но она может контролировать, когда они проснутся. Python принудительно погружает все потоки, кроме текущего, в сон. Они спят и ждут светлого будущего GIL.
своих потоков, но она может контролировать, когда они проснутся. Python принудительно погружает все потоки, кроме текущего, в сон. Они спят и ждут светлого будущего GIL. А когда Python понимает, что текущий поток уже слишком долго работает в гордом одиночестве, - он усыпляет его в ожидаемом месте и будит один из своих спящих потоков.
потоки неожиданно, и ей нет дела до того, что Python хочет свой текущий поток усыпить в ожидаемое время. Что же делать? Нужно интегрироваться в операционную систему и поменять логику, по которой она усыпляет и будит потоки!
потоки неожиданно, и ей нет дела до того, что Python хочет свой текущий поток усыпить в ожидаемое время. Что же делать? Нужно интегрироваться в операционную систему и поменять логику, по которой она усыпляет и будит потоки! Плохая идея.
потоки неожиданно, и ей нет дела до того, что Python хочет свой текущий поток усыпить в ожидаемое время. Что же делать? Нужно, чтобы текущий поток уснул сам. В ожидаемое время.
мы находимся внутри C-кода интерпретатора. • Автор Python решил, что текущий поток будет засыпать раз в 100 тиков, где тик примерно соответствует одной инструкции интерпретатора. • sys.getcheckinterval, sys.setcheckinterval
а 5 мс. • Проснувшийся по таймауту поток обижается, выставляет флаг “хочу GIL” и снова засыпает. • Текущий поток в конце каждого тика проверяет наличие флага “хочу GIL” и, если таковой есть, будит один из ожидающих GIL потоков и засыпает. • sys.getswitchinterval, sys.setswitchinterval
засыпают и просыпаются по велению операционной системы - часто и неожиданно. • GIL запрещает всем потокам Python, кроме текущего, просыпаться неожиданно. • Текущий поток засыпает и просыпается по велению операционной системы. Но когда Python решает, что пора переключаться, - текущий поток засыпает сам.
скачивание файлов. Они не смогут просыпаться часто в случайный момент времени и будут качать с той же скоростью, как если бы были запущены последовательно?
скачивание файлов. Они не смогут просыпаться часто в случайный момент времени и будут качать с той же скоростью, как если бы были запущены последовательно? А вот и нет. В Python все предусмотрено.
неожиданно уснет, поток B неожиданно проснется и поменяет байтики внутри интерпретатора, которые поток А ну никак не ожидает видеть измененными. Но если в потоке вызвана функция операционной системы для приема данных по сети - это не код python, и он не может поменять байтики в интерпретаторе.
внешней библиотеки, он отключает механизм GIL. А после того как функция вернет управление, снова включает его. Это называется “поднять GIL” и “опустить GIL”.
мешает нашим потокам и приносит пользу: • Код, не имеющий отношения к интерпретатору, засыпает и просыпается, когда ему надо, и не ждет ничего лишнего. • Python-код засыпает и просыпается аккуратно, чтобы ничего не повредилось.
и вы хотите максимизировать скорость выполнения именно Python кода — значит, у вас очень редкий случай :) • NumPy и SciPy поднимают GIL на долгих операциях (работа с матрицами). • Используйте библиотеки для расчетов • Используйте процессы.