- PVSM.RU - https://www.pvsm.ru -
Как известно, в основной реализации Питона CPython (python.org [1]) используется Global Interpreter Lock (GIL). Эта штука позволяет одновременно запускать только один питоновский поток — остальные обязаны ждать переключения GIL на них.
Коллега Qualab [2] недавно опубликовал на Хабре бойкую статью [3], предлагая новаторский подход: создавть по субинтерпретатору Питона на поток операционной системы, получая возможность запускать все наши субинтерпретаторы параллельно. Т.е. GIL как бы уже и не мешает совсем.
Идея свежая, но имеет один существенный недостаток — она не работает…
Позвольте мне сначала рассмотреть GIL чуть подробней, а потом перейдем к разбору ошибок автора.
Тезисно опишу существенные для рассмотрения детали GIL в реализации Python 3.2+ (более подробное изложение предмета можете найти тут [4]).
Версия 3.2 выбрана для конкретики и сокращения объема изложения. Для 1.x и 2.x отличия незначительны.
Как видим, запускать одноременное параллельное выполнение кода можно, нельзя при этом делать вызовы Python C API (это касается выполнения кода написанного на питоне тоже, естественно).
При этом «нельзя» означает (особенно в RELEASE сборке, используемой всеми) что такое поведение нестабильно. Может и не сломаться сразу. Может на этой программе вообще работать замечательно, а при небольшом безобидном изменении выполняемого питоновского кода завершаться с segmentation fault и кучей побочных эффектов.
Что же делает коллега Qualab [2] (ссылку на архив с кодом можете найти в его статье, исходник я продублировал на gist: gist.github.com/4680136 [5])?
В главном потоке сразу же отпускается GIL через PyEval_SaveThread() [6]. Главный поток больше с питоном не работает — он создает несколько рабочих потоков и ждет их завершения.
Рабочий поток захватывает GIL [7]. Код вышел странноватым, но сейчас это не принципиально. Главное — GIL зажат у нас в кулаке.
И сразу же параллельное исполнение рабочих потоков превращается в последовательное. Можно было и не городить конструкцию с субинтерпретаторами — толку от них в нашем контексте ровно ноль, как и ожидалось.
Не знаю, почему автор этого не заметил сразу, до опубликования статьи. А потом долго упорствовал, предпочитая называть черное белым.
Вернуться к параллельному исполнению просто — нужно отпустить GIL. Но тогда нельзя будет работать с интерпретатором Питона.
Если всё же наплевать на запрет и вызывать Python C API без GIL — программа сломается, причем не обязательно прямо сразу и не факт что без неприятных побочных эффектов. Если хотите выстрелить себе в ногу особенно замысловатым способом — это ваш шанс.
Повторюсь опять: GIL один на весь процесс, не на интерпретатор-субинтерпретатор. Захват GIL означает, что все потоки выполняющие питоновский код приостановлены.
Нравится GIL или не очень — он уже есть и я настоятельно рекомендую научиться правильно с ним работать.
Правила простые, исключений и обходных лазеек нет.
Автор: svetlov
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/26332
Ссылки в тексте:
[1] python.org: http://python.org
[2] Qualab: http://habrahabr.ru/users/qualab/
[3] бойкую статью: http://habrahabr.ru/post/167261/
[4] тут: http://asvetlov.blogspot.com/2011/07/gil.html
[5] gist.github.com/4680136: https://gist.github.com/4680136
[6] отпускается GIL через PyEval_SaveThread(): https://gist.github.com/4680136#file-gistfile1-txt-L25
[7] захватывает GIL: https://gist.github.com/4680136#file-gistfile1-txt-L48
[8] multiprocessing: http://docs.python.org/2/library/multiprocessing.html
[9] Источник: http://habrahabr.ru/post/167733/
Нажмите здесь для печати.