Pull to refresh

Comments 93

За старание плюсик. А так — слишком много слов для того, чтобы показать как создавать и запускать поток.
Судя по оговоркам, автор знает гораздо больше чем пишет :)
Да, пожалуй, но хотелось всего по чуть-чуть ухватить.
Присоединяюсь к предыдущему комментатору. Очень хороший обзор, мне, как начинающему питонисту, пригодится на будущее, думаю. Но примеров на multiprocessing и subprocess (хотя там непонятно, что там писать) для полноты картины не хватает.
Я, честно говоря, не ожидал, что эта тема стоит настолько остро. В следующих статьях я постараюсь подробнее рассказать о каждом виде параллелизма стандартными средствами питона.
Уже жду с нетерпением.
Спасибо за статью. Прошло 2 года. Мы все еще ждем продолжения.
Привет из 2015 года. Все еще ждем.
Привет из 2016 года. Видимо, вы хер положили на эту тему, но многие все еще ждут.
Надеюсь к 2017 вы смиритесь или найдете другие источники.
Первым по поиску в гугле по теме выдается этот тред. Подхватываю эстафету в 2017м!
2018 год с вами) Ждем.
Я уже сам разобрался)
Пишу вам из 2034 года. Все еще нет продолжения.

2020, С Новым Годом! Ждём)

2021 на связи! Что с продолжением?)
UFO just landed and posted this here
1) Да, это не совсем гонка, но эффект похожий.
2) Точно оценивать производительность задачи не стояло. Хотелось придумать показательный пример, но при этом максимально простой. Как мне кажется для академических целей вполне пойдет.
UFO just landed and posted this here
Момент спорный, но вы точно правы в том, что такой пример может только запутать.
Упоминание про гонку я убрал из статьи.
UFO just landed and posted this here
Пожалуй я неправильно выразил мысль. Это не GIL лучше, а без GIL пока не получается лучше.

Еще тогда Гвидо сказал, что с радостью уберет GIL, если кто-то предложит реализацию без GIL, но такую, чтобы она не ухудшала производительность однопоточных программ. С тех пор ситуация, вроде бы, не сильно изменилась.
Если честно — то GIL таки довольно серьёзно улучшился в 3.2
Да, я так поглядываю мельком. Там вроде бы появилась хитрая оптимизация с задержками на 5 миллисекунд, но пока Python 3 для многих не актуален и приходится работать с тем что есть сейчас =)

Но с другой стороны я к GIL отношусь философски: если производительность сильно критична, то я могу написать критические места на Си, но пока на моих задачах куда важнее надежность и предсказуемость кода.
UFO just landed and posted this here
1. Global Lock — необходимое зло. Пока от него не удается избавится.
2. В 3.2 GIL переписали. Он никуда не делся, но стало гораздо меньше холостых захватов в eval loop:
когда поток захватывает GIL, понимает что вообще-то он не должен работать потому что в блокировке и отпускает GIL. Раньше подобное случалось часто, теперь GIL не дергается если переключать поток не нужно. Вот здесь asvetlov.blogspot.com/2011/07/gil.html я описывал как оно сейчас работает, а здесь перевод Beazley habrahabr.ru/post/84629/ — почему раньше работало хуже.

Вопль «какого чёрта» улыбнул.
1. Если что, даже в glibc есть global state, защищенный блокировками. Пример — менеджер памяти, все эти malloc() и free().
2. Глобальное состояние интерпретатора имеет пару дюжин разных локов. import, zlib и io — у каждого свои блокировки помимо GIL.
3. Программы, не требующие явной синхронизации, возможны. Правда, в любом нетривиальном случае их приходится писать или очень аккуратно, или выбирая необычные методики (я имею в виду Erlang в первую очередь). С Питоном так не получится.
4. Новый GIL работает на CAS'ах если что.

Итого. GIL — не манна небесная. Решает часть проблем, выдвигает новые. Питон от версии к версии старается сделать работу с потоками лучше. Есть над чем потрудиться.
Быстрее с GIL не выйдет при всём желании. Вопрос лишь в том, насколько получается медленней при разных реализациях.
UFO just landed and posted this here
Используйте поменьше пафоса, пожалуйста. И приводите примеры.
Мне трудно обсуждать анонимную нормальную платформу. Готов заранее признать, что эта невидимка не имеет ни одной проблемы и замечательно подходит для всех мыслимых случаев сейчас и в необозримом будущем.
UFO just landed and posted this here
UFO just landed and posted this here
Жесть. При чем тут Java? Она тоже является интерпретатором?
UFO just landed and posted this here
Вы считаете Гвидо и разработчики Питона тупее вас?
UFO just landed and posted this here
А кто говорит что GIL ускоряет однопоточные программы?

Просто сравнивать Jav-у, С++ с Питоном нет смысла — это разные вещи. Питон — четкий однопоточный интерпретатор, по-крайней мере в классической реализации.

GIL — это просто решение, позволяющее разработчику абстрагироваться от потокобезопасности нативного кода, стоящего за инструкциями Питона. Для интерпретируемого языка для общих задач — вполне нормальное решение.
UFO just landed and posted this here
В Питоне принято для максимальной загрузки CPU масштабировать на процессы. Это такая альтернатива. Посмотрите на Google Chrome как наиболее доступный пример масштабирования процессов.
UFO just landed and posted this here
Что вы подразумеваете под «вертикальной масштабируемостью»?
UFO just landed and posted this here
Потоки тяжелее в обслуживании чем процессы, потому что требуют затрат на синхронизацию адресного пространства процесса, в котором они работают.
UFO just landed and posted this here
IPC, конечно же дороже. Процессы в этом случае не используют. Процессы используют, например, при организации архитектуры сервера веб-приложений для параллельной обработки потока запросов.

Я просто о том, что использование нельзя использовать нативные потоки для реализации параллельных алгоритмов — когда вы пытаетесь запустить их несколько тысяч, затраты процессорного времени на их запуск и обслуживание нивелируют весь профит от их использования. А если их ещё и синхронизировать надо, то тут вообще всё плохо: синхронизация потоков — «тяжёлая» операция. Для таких вещей используют CUDA, OpenCV или OpenMP, которые позволяют запускать процедуры в чисто параллельном режиме. Но и там куча своих ограничений.
UFO just landed and posted this here
Честно сказать, мне потоки тоже кажутся костылем.
Структурно стройное решение — акторы, взаимодействие на передаче сообщений на не на разделяемой памяти.
Только не доросли мы до массового применения такого подхода.
UFO just landed and posted this here
Реальное железо ничего не знает о переменных, классах и байткоде, который крутится внутри виртуальной машины. Так что не вижу большого противоречия.
Хм, представьте себе одноядерную однопроцессорную машину. Что будет быстрее выполняться на этой машине: программа которая исполняет параллельный алгоритм в многопоточном режиме или программа которая исполняет этот же алгоритм в последовательном режиме.
Да что ж вы всё потоки да потоки :)

Процессами всё масштабируется. В веб-приложениях это прекрасно работает.
О том и статья: потоки в питоне не про производительность на мультиядерных системах.
Ну а производительные многопоточные решения на мультиядерных системах никто на Питоне и не пишет: для это есть C, Go и Java, CUD'ы всякие, OpenMP и прочее.
А вто это уже правильное замечание.
И проблема не только в том чтобы убрать GIL. Нужно еще на поломать существующие библиотеки — иначе этот язык будет называться уже не Питоном.
UFO just landed and posted this here
Есть библиотеки, написанные на чистом Питоне — и там более или менее всё хорошо. А есть использующие C Extensions. Таких очень много, а стоимость их сопровождения гораздо выше чем pure python.
И C Extensions на Jython или IronPython подключать нельзя.
В pypy появилась экспериментальная поддержка. Работает почти всегда, но ужасно медленно.
Завидую вашей непосредственности.
Вы очень непринужденно смешиваете виртуальные машины с языками программирования, произвольно применяете термин «многопоточность» там, где подразумевается более общий «параллелизм».
Многопоточные PHP, PERL и Erlang — мне понравилось.
Какое виртуозное жонглирование терминами!
А в чем вы здесь видите некорректность? Вопрос был поставлен так: почему в 2012 году, когда космические корабли даже в моем нетбуке 4 ядра, в RI реализации одного из популярнейших языков программирования до сих пор живет такой ужас царизма как GIL. И не просто живет, а еще и считается чуть ли не священной коровой, без которой этот язык жить не будет.

В ответ идет какой-то не очень текст, что, мол, иначе нельзя — не получится написать интерпретатор без GIL. Оппонент приводит вам пример: вот есть интерпретаторы, как JVM, CLR. Они уже давно очень даже неплохо обходятся без GIL. Позволяют писать действительно параллельный код. То, что JVM интерпретирует не саму яву, а байт-код — это ни разу не принципиально в данном контексте: вы легко можете дописать javac на самой яве, и запустить в той же JVM. В свое время контейнеры сервлетов так делали для исполнения JSP (может, и сейчас делают — не знаю) — компилировали JSP в сервлет на яве, компилировали его в байт-код, и загружали получаемый класс. Многие интерпретируемые языки поверх JVM опускают (позволяют опускать) явный этап компиляции в байт-код: я могу навскидку вспомнить BeanShell и JavaScript (rhino). Да и тот же самый Jython, кстати.

В этом смысле мне лично не очень понятно, что именно вы имеете против сравнения Python RI с JVM, CLR PHP/Perl runtime. По-моему, это вполне легальное сравнение, которое наглядно показывает, что интерпретатор без GIL более чем реален, и очень даже производительный (если сомневаетесь — можете сравнить Python RI с JVM :)
Вот так — правильно. Сравнивать нужно VM CPython, JVM и CLR.
Говорить о С/С++ не следовало. Совсем другая область. Даже не из-за байткода и VM а из-за отсутствия garbage collector.
PHP, PERL и Erlang тоже не следовало приводить в качестве примера «правильных» и «нормальных» реализаций — они несколько вольно работают с потоками. Если брать за идеал JVM или CLR — Erlang и PERL убоги.

Если говорить, что VM CPython хуже JVM потому что не умеет делать честную правильную многопоточность — соглашусь. Другой вопрос, что это ограничение можно обойти, что и делают если нужно особо не напрягаясь.
Я не думаю что GIL считается «священной коровой»: просто была бы возможность сделать pure-параллельный Питон, без GIL, нативно написанный на C (а не «слоёный», как JPython и IronPython), то её реализуют. Просто сейчас наличие GIL не считается вселенским злом, которое мешает жить Питон-программистам. Ну есть GIL, и есть. Язык отличный, приложения на нём пишутся и прекрасно работают. А если надо реализовать какое-нибудь более производительное решение — берётся C и реализуется.
Насколько я понимаю, просто GIL был в питоне так долго, что теперь его не так-то просто выковырять. Т.е. это беда RI, да, но а) решить ее не так-то просто б) за долгое время питонисты научились с этим жить, поэтому особо и не жалуются. Как следствие — решать эту проблему особо и не торопятся. Ну и, все-таки, у опен-сорс коммьюнити питона нет таких ресурсов, как у Sun/MS, чтобы потратить десятки человеко-лет на написание высокопроизводительной VM.
Ну кто его знает. Может Гвидо и Ко уже вынашивают в своих головах планы на 4-й Питон: с блэкджеком без GIL'а и с потоками :)
Абсолютно верно по всем пунктам.
Ну, справедливости ради, в JVM тоже куча важного делается только на savepoint-ах, в режиме «вселенная подождет».

Вообще, доколе Azul будет хвастаться полностью параллельным GC, а Sun, создатель явы, будет допиливать state-of-art G1, который все еще требует stop-the-world?

Не надо обижать маленьких :) Кто-то писал, что «concurrent programming is hard» — вот в питоне без него и обходятся, так проще :)
UFO just landed and posted this here
насколько я понял GIL присутствует не во всех реализациях питона, но там, где он есть, полноценной многопоточности не будет, как бы быстр не был GIL.

http://lua-users.org/wiki/LuaVersusPython
Верно. Если уж упомянут lua, но в нём можно запустить несколько потоков, но я бы не назвал это мультипоточной моделью исполнения. Lua запускает новый интерпретатор в каждом потоке, по одной независимой виртуальной машине на поток (с возможностью кроссинтерпретаторного взаимодействия). Так же работает и Perl.
Это другая модель параллельных вычислений, имеющая как достоинства так и недостатки.
глубоко я не копал, но ничего страшного нет в этом, луа сравнительно легкий интерпретатор, а кроссинтерпретаторное взаимодействие другими словами — это взаимодействие между потоками, при чем синхронизация будет только там, где надо (например, защита данных). Кроме этого, есть выбор: запускать интерпретатор связав с уже существующими(передав lua_State) или без связи(независимо). В питоне же один GIL на весь процесс.
Спасибо за статью, совсем недавно столкнулся с этой проблемой, как-то сам дошел, что нужно использовать multiprocessing, но шел долго, теперь знаю почему так
Спасибо большое за статью. Стоит заметить, что многопоточности питона хватает выше крыши, для того чтобы распараллелить эмуляцию действий различных пользователей на сайте, а также тестирование любого массового сетевого взаимодействия. Мало того, тут GIL даже помогает, мы видим в каком потоке остановились во время отладки и видим стек. Насколько я помню ограничения GIL позволяют сериализовать состояние потока, включая его имя, а также позволяет спокойно отлаживаться в одном потоке, так что остальные потоки в этот момент не мешают.
Спасибо за статью, жаль только, что нет примеров использования multiprocessing.
Программа с тредами запущенная руками из консоли не обрабатывает ctrl+c
Как ее научить этому?
Писал многопоточный скрипт на python, экспериментальным путем было установлено, что потоки не раскидываются на ядра. Вы уверены, что за это отвечает ОС?
из ссылки выше:
When any one specific thread is running in Python code though it will acquire the GIL, in doing this you lock out any other threads which need to have the GIL at that time when running. Thus, although you can have multiple threads at the same time, for Python bound code you can't effectively run them concurrently. This means for example that two Python threads can't at the same time make use of two distinct CPUs or cores in the system.
Доки я читал, спасибо. Вообще, не раз встречал мнение программистов на питоне, что потоки реализованы «чтобы было». Типа как «go to» в ЯП. Меня скорость работы моей программы, разбитой на потоки, совсем не порадовало. И, вы не поверите, задача была решена на perl.
1) Вы так говорите, как будто я раскаленными вилами вас пытаю и заставляю писать на Python, причем абсолютно все.

2) В Python потоки изначально не создавались ради производительности. Они для создания асинхронных I/O и вызовов внешних процессов. Если проблема в этом, то в питоне есть куча методов решения вопросов производительности. Если просто не нравится питон, то хозяин барин.
Лично я критичное по производительности приложение буду писать на си/с++.
Если мне просто надо написать тупой мониторинг 10 серверов, то можно писать хоть на брейнфаке (я кстати иногда такие вещи пишу на Прологе, работает медленно, зато пишется в 2-3 строки то, что даже на перле может занять строк 20).

3) Перл я уважаю, хороший язык писал на нем лет 10 назад. Питон изучал параллельно. Сложно сказать, почему я в итоге выбрал питон. Сейчас-то комьюнити у питона просто огромно, а тогда перл был круче и считался чуть ли не совершенным языком (ну или может быть я идеализирую, мне тогда было 14 лет :)
Первый человек, которому нужна производительность многопоточного приложения и выбирающий между Python и Perl. При желании можно создать неуправляемый поток ОС напрямую через API платформы. Причём я бы посоветовал писать C-extension, если уж нужна и производительность и функционал питона.
Нет, в том проекте мне нужна не производительность, а одновременность выполнения некоторых длительных действий. Т.е. чтобы несколько сотен потоков крутились асинхронно, что, как мы знаем, в силу GIL невозможно.
Потоки в Питоне реализованы асинхронно. Просто из-за того что интерпретатор работает в один поток, ему приходится переключаться между потоками в ходе работы. Вот для этого и реализован GIL — инструкция в коде потоке захватывает GIL, выполняется, отпускает GIL. А сами потоки при этом параллельны.

Это похоже на то, как работает операционная система на одноядерной однопроцессорной машине: там ведь тоже потоки не работают в настоящем параллельном режиме: процессор переключается между ними по очереди согласно их приоритету.
Ну, собственно, я это и имел виду. Нам-то от такой асинхронности ни жарко, ни холодно.
А вы думаете что даже если на 8-ядерной машине запустите 100 потоков они у вас будут в чисто параллельно работать?
Конечно, не «чисто», но гораздо быстрее. Повторюсь, одна и та же задача решалась на питоне и перле. Перл вин :)
Еще можно, например, реализовать внутренность потоков на с/с++ и спилить их в модуль (если использовать SWIG, то это делается очень просто).

А потом запустить что-то такое:
from threading import Thread
from test_loop import loop

threads = [Thread(target=loop) for _ in xrange(1000)]
map(lambda x: x.start(), threads)
map(lambda x: x.join(), threads)


Внутри loop у меня что-то типа алгоритма Флоида крутится. 1000 потоков весьма бодро грузят ядра.
В каждом потоке по 8'000'000 итераций цикла. Код отрабатывает за 18-19 сек.

Вообще лично мой рецепт такой:

Да, возможно на Perl вы выиграете на тридах производительности на 10% (хотя даже в это я не поверю) по сравнению с многопроцессной реализацией на Python (в которой нет GIL).

Но я лично пишу прототип на Python, если производительность не устраивает, то переписываю медленный код на Си (такой код всегда в конце концов прийдется переписать на Си, проверено много раз всякими играми с разными языками и их подобиями).

Да, может быть этот подход не достаточно гибкий, но хорошо писать на 2-3 языках уже сложно, а знать больше вообще тяжело (я думаю понятно, что речь не о Hello world). Мой джентльменский набор C/C++/Python/JS/Bash.
Да, я знаю, что инструмент подбирается под задачу, но переход на другой инструмент это время и деньги. Куда проще и дешевле просто купить лишний сервер и написать изначально гибкое и расширяемое решение (в чем питону вообще мало равных).

И да, все что я тут написал — мое ИМХО, основанное на личном опыте. Если кто-то думает иначе — я с удовольствием выслушаю.
Спасибо за интересную заметку. До недавнего времени я не знал, что в CPython есть настоящая многопоточность. Если бы я писал книжки по Python, то говорил бы о multiprocessing еще в введении и выделял это жирным шрифтом. Еще мне всегда было интересно, как программисты на Python ищут документацию. Вся документация по Perl к примеру находится в одном месте — metacpan.org/ Если же посмотреть на PyPI, то у половины модулей никакой документации нет вообще (даже если установить модуль и посмотреть в pydoc). Может быть, есть какой-то специальное место для поиска документации по модулям, доступных через PyPI?

Хорошо пишите, продолжайте в том же духе.
UFO just landed and posted this here
Жаль что примеры кода есть только по самым простым и «плохим» реализациям мультипоточности. Неплохо было бы добавить примеров использования модулей subprocess и multiprocessing в соответствующих параграфах статьи.
Sign up to leave a comment.

Articles