Вопрос

Какие модули используются для написания многопоточных приложений на Python?Я осведомлен об основных механизмах параллелизма, предоставляемых языком, а также о Бесстекленный Python, но каковы их соответствующие сильные и слабые стороны?

Это было полезно?

Решение

В порядке возрастания сложности:

Используйте модуль нарезания резьбы

Плюсы:

  • Это очень легко выполнить любую функцию (любого вызываемого на самом деле) в собственной резьбы.
  • Обмен данными-это если очень не просто (замки легко и не бывает :), на не менее простой.

Минусы:

  • Как уже упоминалось автор : Юрген Потоки Python фактически не могут одновременно получать доступ к состоянию в интерпретаторе (есть одна большая блокировка, печально известная Глобальная Блокировка интерпретатора.) На практике это означает, что потоки полезны для задач, связанных с вводом-выводом (создание сети, запись на диск и так далее), но совсем не полезны для выполнения параллельных вычислений.

Используйте многопроцессорная обработка модуль

В простом варианте использования это выглядит точно так же, как использование threading за исключением того, что каждая задача выполняется в своем собственном процессе, а не в своем собственном потоке.(Почти буквально:Если вы возьмете Пример Илая, и заменить threading с multiprocessing, Thread, с Process, и Queue (модуль) с multiprocessing.Queue, все должно работать просто отлично.)

Плюсы:

  • Фактический параллелизм для всех задач (отсутствие глобальной блокировки интерпретатора).
  • Масштабируется до нескольких процессоров, может даже масштабироваться до нескольких Машины.

Минусы:

  • Процессы работают медленнее, чем потоки.
  • Обмен данными между процессами сложнее, чем с потоками.
  • Память не является неявно разделяемой.Вы должны либо явно поделиться им, либо выбрать переменные и отправлять их туда и обратно.Это безопаснее, но и сложнее.(Если это имеет все большее значение, то разработчики Python, похоже, подталкивают людей в этом направлении.)

Используйте модель событий, например Скрученный

Плюсы:

  • Вы получаете чрезвычайно точный контроль над приоритетом, над тем, что и когда выполняется.

Минусы:

  • Даже при наличии хорошей библиотеки асинхронное программирование обычно сложнее многопоточного, как с точки зрения понимания того, что должно происходить, так и с точки зрения отладки того, что происходит на самом деле.

В ВСЕ я предполагаю, что вы уже понимаете многие проблемы, связанные с многозадачностью, в частности, сложную проблему обмена данными между задачами.Если по какой-то причине вы не знаете, когда и как использовать блокировки и условия, вы должны начать с них.Многозадачный код полон тонкостей и подводных камней, и действительно лучше хорошо разобраться в концепциях, прежде чем приступать к работе.

Другие советы

Вы уже получили довольно много ответов, от "поддельных потоков" до внешних фреймворков, но я не видел, чтобы кто-нибудь упоминал Queue.Queue -- "секретный соус" потоковой обработки CPython.

Для расширения:до тех пор, пока вам не нужно перекрывать обработку с высокой нагрузкой на процессор на чистом Python (в этом случае вам нужно multiprocessing -- но у него есть свои собственные Queue реализация тоже, так что вы можете с некоторыми необходимыми предосторожностями применить общий совет, который я даю;-), встроенный в Python threading сделаю...но это сделает это намного лучше, если вы будете им пользоваться намеренно, например, следующим образом.

"Забудьте" об общей памяти, предположительно главном плюсе многопоточности - она плохо работает, плохо масштабируется, никогда не была и никогда не будет.Используйте общую память только для структур данных, которые настраиваются один раз до того, как вы создаете подпотоки и впоследствии никогда не меняетесь - для всего остального создайте одинокий поток, ответственный за этот ресурс, и взаимодействует с этим потоком через Queue.

Выделите специализированный поток для каждого ресурса, который, по вашему обычному мнению, должен быть защищен блокировками:изменяемая структура данных или их связная группа, соединение с внешним процессом (БД, XMLRPC-сервером и т.д.), внешним файлом и т.д. и т.п.Создайте небольшой пул потоков, предназначенный для задач общего назначения, которые не имеют или не нуждаются в выделенном ресурсе такого рода -- не надо создавайте потоки по мере необходимости, иначе накладные расходы на переключение потоков будут перегружать вас.

Связь между двумя потоками всегда осуществляется через Queue.Queue -- форма передачи сообщений, единственная разумная основа для многопроцессорной обработки (помимо транзакционной памяти, которая является многообещающей, но для которой я не знаю ни одной производственной реализации, кроме как в Haskell).

Каждый выделенный поток, управляющий одним ресурсом (или небольшим взаимосвязанным набором ресурсов), прослушивает запросы в определенной очереди.Экземпляр очереди.Потоки в пуле ожидают в одной общей очереди.Очередь (очередь надежно защищена от потоков и не будет подведи тебя в этом).

Потоки, которым просто нужно поставить запрос в очередь в некоторой очереди (общей или выделенной), делают это, не дожидаясь результатов, и движутся дальше.Потоки, которым в конечном итоге действительно нужен результат или подтверждение для запроса, ставят в очередь пару (request, receivingqueue) с экземпляром Queue.Очередь, которую они только что создали, и в конце концов, когда ответ или подтверждение необходимы для продолжения, они получают (ожидание) из своего receivingqueue.Убедитесь, что вы готовы получать ответы об ошибках, а также реальные ответы или подтверждения (Twisted's deferredкстати, они отлично умеют организовывать такого рода структурированный ответ!).

Вы также можете использовать Queue для "парковки" экземпляров ресурсов, которые могут использоваться любым потоком, но никогда не будут совместно использоваться несколькими потоками одновременно (соединения с БД с некоторыми компонентами DBAPI, курсоры с другими и т.д.) - Это позволяет вам снизить требования к выделенному потоку в пользу большего объединения (поток пула, который получает из общей очереди запрос, требующий доступного ресурса, получит этот ресурс из соответствующей приложению очереди, ожидая, если необходимо, и т.д. и т.п.).

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

Но я понимаю, что Twisted не для всех - подход "выделять ресурсы или объединять их, ставить в очередь wazoo, никогда не делать ничего, требующего блокировки или, не дай бог, какой-либо еще более продвинутой процедуры синхронизации, такой как семафор или условие" все еще можно использовать, даже если вы просто не можете разобраться в методологиях, основанных на асинхронных событиях, и все равно обеспечит большую надежность и производительность, чем любой другой широко применимый подход к обработке потоков, на который я когда-либо натыкался.

Это зависит от того, что вы пытаетесь сделать, но я неравнодушен к простому использованию threading модуль в стандартной библиотеке, потому что это позволяет действительно легко взять любую функцию и просто запустить ее в отдельном потоке.

from threading import Thread

def f():
    ...

def g(arg1, arg2, arg3=None):
    ....

Thread(target=f).start()
Thread(target=g, args=[5, 6], kwargs={"arg3": 12}).start()

И так далее.У меня часто есть настройка производителя / потребителя с использованием синхронизированной очереди, предоставляемой Queue модуль

from Queue import Queue
from threading import Thread

q = Queue()
def consumer():
    while True:
        print sum(q.get())

def producer(data_source):
    for line in data_source:
        q.put( map(int, line.split()) )

Thread(target=producer, args=[SOME_INPUT_FILE_OR_SOMETHING]).start()
for i in range(10):
    Thread(target=consumer).start()

Камаэлия это фреймворк Python для создания приложений с множеством взаимодействующих процессов.

(источник: kamaelia.org) Камаэлия - Параллелизм стал полезным и увлекательным занятием

В Камаэлии вы создаете системы из простые компоненты, которые взаимодействуют друг с другом.Это ускоряет разработку, значительно облегчает техническое обслуживание, а также означает, что вы создавайте естественно параллельное программное обеспечение.Он предназначен для того, чтобы быть доступным по Любой разработчик, включая новичков.Это также делает игру веселой :)

Что это за системы?Сетевые серверы, клиенты, настольные приложения, игры на базе pygame, системы транскодирования и конвейеры, системы цифрового телевидения, средства борьбы со спамом, обучающие инструменты и многое другое :)

Вот видео с Pycon 2009.Все начинается с сравнения Камаэлии с Скрученный и Параллельный Python а затем проводит практическую демонстрацию Камаэлии.

Простой параллелизм с Kamaelia - Часть 1 (59:08)
Простой параллелизм с Kamaelia - Часть 2 (18:15)

Что касается Камаэлии, приведенный выше ответ на самом деле не охватывает преимущества здесь.Подход Камаэлии обеспечивает унифицированный интерфейс, который прагматичен, но не идеален, для работы с потоками, генераторами и процессами в единой системе параллелизма.

По сути, это метафора запущенной системы, у которой есть ящики входящих и исходящих.Вы отправляете сообщения на почтовые ящики "Исходящие", и при совместном подключении сообщения перетекают из ящиков "Исходящие" в ящики "входящие".Эта метафора / API остается неизменной независимо от того, используете ли вы генераторы, потоки или процессы или общаетесь с другими системами.

"Не идеальная" часть связана с тем, что синтаксический сахар еще не добавлен для входящих и исходящих сообщений (хотя это обсуждается) - в системе уделяется особое внимание безопасности / удобству использования.

На примере производителя-потребителя, использующего голую потоковую обработку выше, это становится таковым в Kamaelia:

Pipeline(Producer(), Consumer() )

В этом примере не имеет значения, являются ли это потоковые компоненты или нет, единственное различие между ними с точки зрения использования - это базовый класс для компонента.Компоненты генератора взаимодействуют с помощью списков, потоковые компоненты - с помощью очереди.Очереди и процессы, основанные на использовании os.pipes.

Однако причина такого подхода заключается в том, чтобы усложнить создание трудноотлаживаемых ошибок.При потоковой обработке или любом другом параллельном использовании общей памяти проблема номер один, с которой вы сталкиваетесь, - это случайное нарушение обновления общих данных.Используя передачу сообщений, вы устраняете один класс ошибок.

Если вы везде используете голую потоковую обработку и блокировки, вы, как правило, работаете исходя из предположения, что при написании кода вы не допустите никаких ошибок.Хотя мы все стремимся к этому, такое случается очень редко.Объединяя поведение блокировки в одном месте, вы упрощаете то, где что-то может пойти не так.(Обработчики контекста помогают, но не помогают при случайных обновлениях вне обработчика контекста)

Очевидно, что не каждый фрагмент кода может быть написан в виде передачи сообщений и общего стиля, поэтому у Kamaelia также есть простая программная транзакционная память (STM), которая является действительно отличной идеей с неприятным названием - это больше похоже на контроль версий для переменных, т. Е. проверяет некоторые переменные, обновляет их и фиксирует обратно.Если у вас возникнет коллизия, вы промойте и повторите процедуру.

Релевантные ссылки:

В любом случае, я надеюсь, что это полезный ответ.FWIW, основная причина настройки Kamaelia заключается в том, чтобы сделать параллелизм более безопасным и простым в использовании в системах python, без виляния хвостом собаки.(то есть большое ведро с компонентами

Я могу понять, почему другой ответ Камаэлии был изменен, поскольку даже для меня он больше похож на рекламу, чем на ответ.Как автору Kamaelia приятно видеть энтузиазм, хотя я надеюсь, что это содержит немного более релевантный контент :-)

И это мой способ сказать, пожалуйста, примите во внимание, что этот ответ по определению предвзят, но для меня цель Камаэлии - попытаться обобщить то, что является лучшей практикой IMO.Я бы посоветовал опробовать несколько систем и посмотреть, какая из них подходит именно вам.(также, если это неуместно для переполнения стека, извините - я новичок на этом форуме :-)

Я бы использовал микропотоки (тасклеты) Stackless Python, если бы мне вообще приходилось использовать потоки.

Вся онлайн-игра (массовый мультиплеер) построена на основе Stackless и ее принципа многопоточности - поскольку оригинал предназначен только для замедления игры из-за свойства массового мультиплеера.

Потоки в CPython широко не поощряются.Одной из причин является GIL - глобальная блокировка интерпретатора, - которая сериализует потоковую обработку для многих частей выполнения.Мой опыт показывает, что таким образом действительно сложно создавать быстрые приложения.Мои примеры кодировок, где все медленнее с потоковой обработкой - с одним ядром (но большое количество ожиданий ввода должно было обеспечить некоторое повышение производительности).

С CPython лучше использовать отдельные процессы, если это возможно.

Если вы действительно хотите испачкать руки, вы можете попробовать использование генераторов для подделки сопрограмм.Вероятно, это не самый эффективный способ с точки зрения выполняемой работы, но сопрограммы предлагают вам очень тонкий контроль кооперативный многозадачность, а не упреждающую многозадачность, которую вы найдете в других местах.

Одно из преимуществ, которое вы обнаружите, заключается в том, что по большому счету вам не понадобятся блокировки или мьютексы при использовании совместной многозадачности, но более важным преимуществом для меня была почти нулевая скорость переключения между "потоками".Конечно, говорят, что Stackless Python также очень хорош для этого;а еще есть Эрланг, если этого не произойдет иметь быть Питоном.

Вероятно, самым большим недостатком совместной многозадачности является общее отсутствие обходного пути для блокировки ввода-вывода.И в поддельных сопрограммах вы также столкнетесь с проблемой, заключающейся в том, что вы не можете переключать "потоки" ни с чего, кроме верхнего уровня стека внутри потока.

После того как вы создадите хотя бы немного сложное приложение с поддельными сопрограммами, вы действительно начнете ценить работу, связанную с планированием процессов на уровне операционной системы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top