Для мене це насправді досить просто:
Варіант підпроцесу :
subprocess
призначений для запуску інших виконуваних файлів --- це в основному обгортка навколо os.fork()
і os.execve()
з деякою підтримкою додаткової сантехніки (налаштування PIPE до та з підпроцесів. Очевидно, ви могли б використовувати інші механізми міжпроцесорного зв’язку (IPC), такі як сокети, Posix або SysV спільна пам'ять, але ви будете обмежені будь-якими інтерфейсами та IPC-каналами, які підтримуються програмами, які ви телефонуєте.
Зазвичай, людина використовує будь-яку subprocess
синхронно --- просто викликає якусь зовнішню утиліту і читає її вихід або чекає завершення (можливо, читаючи його результати з тимчасового файлу або після того, як він розмістить їх у якійсь базі даних).
Однак можна породити сотні підпроцесів і запилити їх. Мій власний улюблений клас утиліти робить саме це.
Найбільший недолік в subprocess
модулі є те , що підтримка введення / виведення , як правило блокування. Існує проект PEP-3145, щоб виправити це в деякій майбутній версії Python 3.x та альтернативній системі asyncproc (Попередження, яке веде право до завантаження, а не до будь-якої документації та README). Я також виявив, що досить просто імпортувати fcntl
та маніпулювати Popen
дескрипторами файлів PIPE безпосередньо, хоча я не знаю, чи є це портативним для не-UNIX-платформ.
(Оновлення: 7 серпня 2019: Підтримка Python 3 для підпроцесів айнсіо : підпроцеси асинціо )
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()
- це подія --- або одна, що включає вхід (доступний для читання) на деякій кількості розеток або дескрипторів файлів, або буферний простір, який стає доступним для деяких інших (доступних для запису) дескрипторів або розеток, деякі виняткові умови (TCP наприклад, пакети PUSH'd поза діапазону або TIMEOUT.
Таким чином, модель програмування Twisted побудована навколо обробки цих подій, а потім цикла на отриманому «головному» обробнику, що дозволяє йому передавати події вашим обробникам.
Я особисто вважаю це ім'я, скручене як сприятливе для моделі програмування ... оскільки ваш підхід до проблеми повинен бути, в деякому сенсі, "скрученим" зсередини. Замість того, щоб мислити свою програму як низку операцій над вхідними даними та результатами чи результатами, ви пишете свою програму як послугу чи демон і визначаєте, як вона реагує на різні події. (Насправді основним "головним циклом" програми Twisted є (як правило? Завжди?) А reactor()
).
В основних виклики з використанням Twisted включає скручування розуму навколо керованих подій моделі , а також уникати використання будь-яких бібліотек класів або наборів інструментальних засобів , які не писані співпрацювати в Twisted рамок. Ось чому Twisted постачає власні модулі для обробки протоколів SSH, для прокльонів та власних функцій підпроцесу / Popen та багатьох інших модулів та обробників протоколів, які, спочатку червоніють, здавалося б, дублюють речі в стандартних бібліотеках Python.
Я думаю, що корисно зрозуміти Twisted на концептуальному рівні, навіть якщо ви ніколи не збираєтесь ним користуватися. Він може дати розуміння продуктивності, суперечок та обробки подій у вашому обробці різьбленням, багатопроцесорною та навіть підпроцесовою, а також будь-якій розповсюдженій обробці, яку ви здійснюєте.
( Примітка. Новіші версії Python 3.x включають функції asyncio (асинхронний введення / виведення), такі як async def , декоратор @ async.coroutine та очікуване ключове слово та вихід від майбутньої підтримки. Усі вони приблизно схожі на Скручений з точки зору процесу (багатозадачність кооперативу). (Для поточного статусу Twisted підтримки для Python 3 ознайомтесь: https://twistedmatrix.com/documents/current/core/howto/python3.html )
Розподілений варіант:
Ще одна сфера обробки, про яку ви не питали, але яку варто врахувати, - це розподілена обробка. Існує багато інструментів і рамок Python для розподіленої обробки та паралельних обчислень. Особисто я вважаю, що найпростішим у користуванні є той, який найменш часто вважається у цьому просторі.
Побудувати розподілену обробку навколо Redis майже тривіально . Весь запас ключів може бути використаний для зберігання робочих одиниць і результатів, Redis LISTs можуть використовуватися Queue()
як об'єкт, а підтримка PUB / SUB може використовуватися для Event
керування подібним чином. Ви можете хешувати ваші ключі та використовувати значення, тиражувані на вільний кластер екземплярів Redis, щоб зберігати топологію та відображення хеш-токенів, щоб забезпечити послідовне хешування та відмову для масштабування за межі можливостей будь-якого одного примірника для координації ваших працівників і дані про марширування (серед них мариновані, JSON, BSON або YAML).
Звичайно, коли ви починаєте створювати масштабніші та складніші рішення навколо Redis, ви повторно реалізуєте багато функцій, які вже вирішені за допомогою, Celery , Apache Spark і Hadoop , Zookeeper , etc.d , Cassandra тощо. Усі вони мають модулі для доступу Python до своїх послуг.
[Оновлення: пара ресурсів для розгляду, якщо ви розглядаєте Python для обчислювально інтенсивного використання в розподілених системах: IPython Parallel та PySpark . Хоча це обчислювальні системи загального призначення, вони є особливо доступними та популярними підсистемами даних та аналітики].
Висновок
Там у вас є гама альтернатив обробки для Python - від однопотокових, з простими синхронними викликами до підпроцесів, пулів опитуваних підпроцесів, потокових та багатопроцесових, керованих подіями кооперативної багатозадачності та виходу до розподіленої обробки.