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