Многопоточность в Python [закрыт]


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

7 73

7 ответов:

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

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

плюсы:

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

плюсы:

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

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

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

плюсы:

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

плюсы:

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

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

плюсы:

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

плюсы:

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

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

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

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

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

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

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

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

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

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

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

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

Это зависит от того, что вы пытаетесь сделать, но я предпочитаю просто использовать 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()

Kamaelia это платформа python для создания приложений с большим количеством взаимодействующих процессов.

http://www.kamaelia.org/cat-trans-medium.png Kamaelia-параллелизм сделал полезным, весело

в Kamaelia построении систем из простые компоненты, которые общаются друг с другом. Это ускоряет развитие, массово помогает обслуживанию, а также означает, что вы построить естественно параллельное программное обеспечение. Это предназначен для доступа по любой для разработчиков, включая новичков. Это также делает его забавным :)

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

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

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

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

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

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

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

Pipeline(Producer(), Consumer() )

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

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

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

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

полезные ссылки:

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

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

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

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

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

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

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

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

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

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

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