Для меня это на самом деле довольно просто:
Опция :
subprocess
- это для запуска других исполняемых файлов --- в основном это обертка вокруг os.fork()
и os.execve()
с некоторой поддержкой для необязательного подключения (настройка PIPE для и из подпроцессов. (Очевидно, другие механизмы межпроцессного взаимодействия (IPC), такие как сокеты, разделяемая память SysV и очереди сообщений, могут использоваться - но вы будете ограничены любыми интерфейсами и каналами IPC, поддерживаемыми вызываемыми вами программами) .
Обычно каждый использует subprocess
синхронно - просто вызывая какую-то внешнюю утилиту и считывая ее выходные данные или ожидая ее завершения (возможно, считывая результаты из временного файла или после того, как он отправил их в некоторую базу данных).
Однако можно порождать сотни подпроцессов и опрашивать их. Моя собственная любимая утилита classh делает именно это. Самым большим недостатком модуля subprocess
является то, что его поддержка ввода-вывода обычно блокируется. Существует черновой вариант PEP-3145 , чтобы исправить это в некоторых будущих версиях Python 3.x и альтернативном asyncproc (Предупреждение, которое приводит к загрузке, а не к какой-либо документации). ни читать). Я также обнаружил, что относительно просто импортировать fcntl
и напрямую манипулировать дескрипторами файлов Popen
PIPE - хотя я не знаю, переносимо ли это на не-UNIX платформы.
subprocess
почти не поддерживает обработку событий ... , хотя вы можете использовать модуль signal
и обычные сигналы UNIX / Linux старой школы - убивая вашего мягко как бы.
Опция многопроцессорная :
multiprocessing
- это для запуска функций в существующем (Python) коде с поддержкой более гибкой связи между этим семейством процессов. В частности, лучше всего построить свой multiprocessing
IPC вокруг Queue
объектов модуля, где это возможно, но вы также можете использовать Event
объекты и различные другие функции (некоторые из которых, по-видимому, построены вокруг поддержки mmap
на платформы, где этой поддержки достаточно).
Модуль Python multiprocessing
предназначен для предоставления интерфейсов и функций, которые очень похожи на threading
, в то же время позволяя CPython масштабировать вашу обработку между несколькими процессорами / ядрами, несмотря на GIL (Global Interpreter Lock). Он использует все мелкозернистые блокировки и согласованность SMP, которые были сделаны разработчиками ядра вашей ОС.
Опция Threading :
threading
- это для довольно узкого диапазона приложений, связанных с вводом / выводом (не требуется масштабирование по нескольким ядрам ЦП) и которые извлекают выгоду из крайне низкой задержки и накладных расходов на переключение переключение потоков (с общей памятью ядра) против переключения процессов / контекстов. В Linux это почти пустой набор (время переключения процессов в Linux очень близко к его переключению потоков).
threading
страдает от двух основных недостатков в Python .
Один из них, разумеется, зависит от конкретной реализации, в основном затрагивая CPython. Это Гил. По большей части большинство программ CPython не выиграют от наличия более двух процессоров (ядер), и часто производительность будет страдать из-за конкуренции за блокировку GIL.
Большая проблема, которая не зависит от конкретной реализации, заключается в том, что потоки совместно используют одну и ту же память, обработчики сигналов, файловые дескрипторы и некоторые другие ресурсы ОС. Таким образом, программист должен быть чрезвычайно осторожен с блокировкой объектов, обработкой исключений и другими аспектами своего кода, которые являются одновременно тонкими и могут убить, остановить или заблокировать весь процесс (набор потоков).
Для сравнения, модель multiprocessing
предоставляет каждому процессу свою собственную память, файловые дескрипторы и т. Д. Сбой или необработанное исключение в любом из них приведет только к уничтожению этого ресурса, и надежная обработка исчезновения дочернего или дочернего процесса может быть значительно проще, чем отладка, изоляция и исправление или обход аналогичных проблем в потоках.
- (Примечание: использование
threading
с основными системами Python, такими как NumPy , может значительно меньше страдать от конфликтов GIL, чем большая часть вашего собственного кода Python. Это потому, что они были специально разработаны для этого).
Опция витая :
Стоит также отметить, что Twisted предлагает еще одну альтернативу, которая одновременно является элегантной и очень сложной для понимания . В основном, рискуя чрезмерно упростить до такой степени, что поклонники Twisted могут штурмовать мой дом вилами и факелами, Twisted обеспечивает совместную многозадачность, управляемую событиями, в рамках любого (одного) процесса.
Чтобы понять, как это возможно, следует прочитать о функциях select()
(которые могут быть построены на select () или poll () или подобных системных вызовах ОС ). По сути, все это обусловлено возможностью сделать запрос ОС на ожидание какой-либо активности в списке файловых дескрипторов или некоторого тайм-аута.
Пробуждение от каждого из этих вызовов к select()
- это событие - либо одно, включающее ввод (доступный для чтения) для некоторого числа сокетов или файловых дескрипторов, или буферное пространство, доступное для некоторых других (доступных для записи) дескрипторов или сокетов некоторые исключительные условия (например, внеполосные PUSH-пакеты TCP) или TIMEOUT.
Таким образом, модель программирования Twisted построена вокруг обработки этих событий, а затем зацикливания на получающемся «главном» обработчике, что позволяет ему отправлять события вашим обработчикам.
Я лично думаю о названии Twisted как о модели программирования ... поскольку ваш подход к проблеме должен быть, в некотором смысле, "вывернутым" наизнанку , Вместо того, чтобы воспринимать вашу программу как последовательность операций с входными данными и выходами или результатами, вы пишете свою программу как сервис или демон и определяете, как она реагирует на различные события. (На самом деле основной «основной цикл» витой программы - это (обычно? Всегда?) reactor()
.
Основные проблемы , связанные с использованием Twisted , заключаются в том, чтобы обернуть ваш разум вокруг модели, управляемой событиями, а также отказаться от использования каких-либо библиотек классов или наборов инструментов, которые не написаны для взаимодействия в рамках Twisted. Вот почему Twisted предоставляет свои собственные модули для обработки протокола SSH, curses и свои собственные функции подпроцесса / popen, а также многие другие модули и обработчики протоколов, которые, на первый взгляд, кажутся дублирующими в стандартных библиотеках Python.
Я думаю, что полезно понимать Twisted на концептуальном уровне, даже если вы никогда не собираетесь его использовать. Он может дать представление о производительности, конфликтах и обработке событий в потоке, многопроцессорной обработке и даже обработке подпроцессов, а также в любой распределенной обработке, которую вы выполняете.
( Примечание: Более новые версии Python 3.x включают функции asyncio (асинхронный ввод / вывод), такие как async def , @ async.coroutine декоратор и ключевое слово await и доходность от будущей поддержки . Все они примерно похожи на Twisted из процесса ( кооперативная многозадачность) перспектива).
Опция распределенная :
Еще одна область обработки, о которой вы не спрашивали, но которую стоит рассмотреть, - это распределенная обработка. Существует множество инструментов и сред Python для распределенной обработки и параллельных вычислений. Лично я считаю, что проще всего использовать тот, который реже считается в этом месте.
Практически тривиально построить распределенную обработку вокруг Redis . Все хранилище ключей может использоваться для хранения рабочих единиц и результатов, списки Redis могут использоваться как Queue()
-подобный объект, а поддержка PUB / SUB может использоваться для Event
-подобной обработки. Вы можете хешировать свои ключи и использовать значения, реплицированные в свободном кластере экземпляров Redis, для хранения топологии и отображений хеш-токенов, чтобы обеспечить согласованное хеширование и отработку отказа для масштабирования, выходящего за пределы возможностей любого отдельного экземпляра для координации ваших работников. и маршалинг данных (маринованные, JSON, BSON или YAML) среди них.
Конечно, когда вы начинаете создавать более масштабное и более сложное решение для Redis, вы заново реализуете многие функции, которые уже были решены с помощью Celery , Apache Spark и Hadoop , Zookeeper , etcd , Cassandra и так далее. У этих всех есть модули для доступа Python к их сервисам.
[Обновление: пара ресурсов для рассмотрения, если вы рассматриваете Python для ресурсоемких распределенных систем: IPython Parallel и PySpark . Хотя это распределенные вычислительные системы общего назначения, они являются особенно доступными и популярными подсистемами данных и аналитики].
Заключение
Здесь у вас есть широкий спектр вариантов обработки для Python, от однопоточных, с простыми синхронными вызовами до подпроцессов, пулов опрашиваемых подпроцессов, потоковых и многопроцессорных, управляемых событиями кооперативных многозадачностей и от распределенных до распределенных обработка.