Dla mnie jest to całkiem proste:
Opcja podprocesu :
subprocess
służy do uruchamiania innych plików wykonywalnych - jest to w zasadzie opakowanie dookoła os.fork()
i os.execve()
z pewną obsługą opcjonalnej instalacji wodociągowej (konfigurowanie PIPE do i z podprocesów. Oczywiście można też zastosować inne mechanizmy komunikacji międzyprocesowej (IPC), takie jak gniazda, Posix lub Pamięć współdzielona SysV, ale będziesz ograniczony do interfejsów i kanałów IPC obsługiwanych przez programy, które wywołujesz.
Zwykle używa się dowolnego narzędzia subprocess
synchronicznie - po prostu wywołując jakieś zewnętrzne narzędzie i odczytując jego dane wyjściowe lub czekając na jego zakończenie (być może czytając jego wyniki z pliku tymczasowego lub po wysłaniu ich do jakiejś bazy danych).
Jednak można stworzyć setki podprocesów i sondować je. Moja ulubiona klasa narzędziowa właśnie to robi.
Największą wadą tego subprocess
modułu jest to, że wsparcie I / O jest zazwyczaj blokując. Istnieje szkic PEP-3145, który naprawi ten problem w niektórych przyszłych wersjach Pythona 3.xi alternatywny asyncproc (ostrzeżenie, które prowadzi prosto do pobrania, a nie do jakiejkolwiek dokumentacji ani pliku README). Odkryłem również, że stosunkowo łatwo jest po prostu zaimportować fcntl
i bezpośrednio manipulować Popen
deskryptorami plików PIPE - chociaż nie wiem, czy jest to przenośne na platformy inne niż UNIX.
(Aktualizacja: 7 sierpnia 2019: obsługa Python 3 dla podprocesów ayncio : podprocesy asyncio )
subprocess
prawie nie obsługuje obsługi zdarzeń ... chociaż możesz użyć signal
modułu i zwykłych sygnałów ze starej szkoły UNIX / Linux - łagodnie zabijając procesy.
Opcja przetwarzania wieloprocesowego :
multiprocessing
służy do uruchamiania funkcji w istniejącym kodzie (Python) z obsługą bardziej elastycznej komunikacji między tą rodziną procesów. W szczególności najlepiej jest budować swój multiprocessing
IPC wokół Queue
obiektów modułu, jeśli to możliwe, ale możesz także używać Event
obiektów i różnych innych funkcji (z których niektóre są prawdopodobnie zbudowane wokół mmap
wsparcia na platformach, na których ta obsługa jest wystarczająca).
multiprocessing
Moduł Pythona ma zapewniać interfejsy i funkcje, które są bardzo podobne do, threading
jednocześnie umożliwiając CPythonowi skalowanie przetwarzania między wieloma procesorami / rdzeniami pomimo GIL (Global Interpreter Lock). Wykorzystuje wszystkie drobnoziarniste blokowanie SMP i wysiłek związany z koherencją, który został wykonany przez programistów jądra twojego systemu operacyjnego.
Opcja gwintowania :
threading
jest przeznaczony do dość wąskiego zakresu aplikacji, które są związane z operacjami we / wy (nie wymagają skalowania na wiele rdzeni procesora) i które korzystają z wyjątkowo niskiego opóźnienia i narzutu przełączania przełączania wątków (ze współdzieloną pamięcią rdzeniową) w porównaniu z procesem / przełączanie kontekstu. W systemie Linux jest to prawie pusty zestaw (czasy przełączania procesów w systemie Linux są bardzo zbliżone do przełączników wątków).
threading
ma dwie główne wady w Pythonie .
Jeden, oczywiście, jest specyficzny dla implementacji - głównie dotyczy CPythona. To jest GIL. W przeważającej części, większość programów CPython nie skorzystają z dostępności więcej niż dwa procesory (rdzenie) i często wydajność będzie cierpieć z niezgody blokującego GIL.
Większy problem, który nie jest specyficzny dla implementacji, polega na tym, że wątki współużytkują tę samą pamięć, programy obsługi sygnałów, deskryptory plików i niektóre inne zasoby systemu operacyjnego. Dlatego programista musi bardzo uważać na blokowanie obiektów, obsługę wyjątków i inne aspekty swojego kodu, które są zarówno subtelne, jak i mogą zabić, zablokować lub zablokować cały proces (zestaw wątków).
Dla porównania multiprocessing
model nadaje każdemu procesowi własną pamięć, deskryptory plików itp. Awaria lub nieobsługiwany wyjątek w którymkolwiek z nich zabije tylko ten zasób, a solidna obsługa zniknięcia procesu potomnego lub rodzeństwa może być znacznie łatwiejsza niż debugowanie, izolowanie oraz naprawianie lub obejście podobnych problemów w wątkach.
- (Uwaga: używanie programu
threading
z głównymi systemami Pythona, takimi jak NumPy , może znacznie mniej cierpieć z powodu rywalizacji GIL niż większość własnego kodu w Pythonie. Dzieje się tak, ponieważ zostały specjalnie zaprojektowane do tego; natywne / binarne części NumPy, na przykład zwolni GIL, gdy będzie to bezpieczne).
Twisted opcja:
Warto również zauważyć, że Twisted oferuje kolejną alternatywę, która jest zarówno elegancka, jak i bardzo trudna do zrozumienia . Zasadniczo, ryzykując zbytnie uproszczenie do punktu, w którym fani Twisted mogą szturmować mój dom z widłami i pochodniami, Twisted zapewnia współpracę wielozadaniową opartą na zdarzeniach w ramach dowolnego (pojedynczego) procesu.
Aby zrozumieć, jak to jest możliwe, powinno się przeczytać o funkcjach select()
(które mogą być budowane wokół select () lub poll () lub podobnych wywołań systemowych OS). Zasadniczo wszystko jest napędzane możliwością wysłania żądania uśpienia systemu operacyjnego w oczekiwaniu na jakąkolwiek aktywność na liście deskryptorów plików lub przekroczenia limitu czasu.
Przebudzenie z każdego z tych wywołań select()
jest zdarzeniem - albo takim, w którym dane wejściowe są dostępne (czytelne) w pewnej liczbie gniazd lub deskryptorów plików, albo przestrzeń buforowa staje się dostępna w innych (zapisywalnych) deskryptorach lub gniazdach, niektóre wyjątkowe warunki (TCP na przykład pozapasmowe pakiety PUSH) lub TIMEOUT.
Tak więc model programowania Twisted jest zbudowany wokół obsługi tych zdarzeń, a następnie zapętlony na wynikowym „głównym” module obsługi, umożliwiając mu wysyłanie zdarzeń do twoich programów obsługi.
Osobiście myślę o nazwie Twisted jako kojarzącej się z modelem programowania ... ponieważ twoje podejście do problemu musi być w pewnym sensie „wypaczone” na lewą stronę. Zamiast wyobrażać sobie program jako serię operacji na danych wejściowych i wyjściach lub wynikach, piszesz program jako usługę lub demon i definiujesz, jak reaguje na różne zdarzenia. (W rzeczywistości podstawową "główną pętlą" programu Twisted jest (zwykle? Zawsze?) A reactor()
).
Do najważniejszych zadań przy użyciu Twisted zaangażować swój umysł skręcania wokół modelu zdarzeniami, a także unikając użycia jakichkolwiek bibliotek klas lub zestawów narzędzi, które nie są napisane współpracować w Twisted ramy. Dlatego Twisted dostarcza własne moduły do obsługi protokołu SSH, dla curses i własnych funkcji podprocesu / Popen, a także wiele innych modułów i programów obsługi protokołów, które na pierwszy rzut oka wydają się powielać rzeczy w standardowych bibliotekach Pythona.
Myślę, że warto zrozumieć Twisted na poziomie koncepcyjnym, nawet jeśli nigdy nie zamierzasz go używać. Może dać wgląd w wydajność, rywalizację i obsługę zdarzeń w twoich wątkach, przetwarzaniu wieloprocesowym, a nawet obsłudze podprocesów, a także w przetwarzaniu rozproszonym, które podejmujesz.
( Uwaga: Nowsze wersje Pythona 3.x są tym asyncio (asynchroniczne I / O) dysponuje takimi jak asynchroniczny def , w @ async.coroutine dekorator, i czekają na słowa kluczowe, a wydajnością z przyszłości obsługiwać wszystkie z nich są zbliżone do. Skręcone z perspektywy procesu (wielozadaniowość kooperacyjna)). (Aby uzyskać aktualny stan obsługi Twisted dla Pythona 3, sprawdź: https://twistedmatrix.com/documents/current/core/howto/python3.html )
Wersja dystrybuowana :
Kolejną dziedziną przetwarzania, o którą nie pytałeś, ale którą warto rozważyć, jest przetwarzanie rozproszone . Istnieje wiele narzędzi i struktur Pythona do przetwarzania rozproszonego i obliczeń równoległych. Osobiście uważam, że najłatwiejszy w użyciu jest taki, który jest najrzadziej uważany za znajdujący się w tej przestrzeni.
Tworzenie rozproszonego przetwarzania wokół Redis jest prawie trywialne . Cały magazyn kluczy może być używany do przechowywania jednostek pracy i wyników, LISTY Redis mogą być używane jako Queue()
podobne obiekty, a obsługa PUB / SUB może być używana Event
do obsługi podobnej do tej. Możesz haszować swoje klucze i używać wartości replikowanych w luźnym klastrze instancji Redis, aby przechowywać topologię i mapowania znaczników skrótu, aby zapewnić spójne mieszanie i przełączanie awaryjne w celu skalowania poza możliwości pojedynczego wystąpienia w celu koordynowania pracowników i organizowanie danych (piklowane, JSON, BSON lub YAML) wśród nich.
Oczywiście, jak zacząć budować większą skalę i bardziej wyrafinowane rozwiązania wokół Redis jesteś ponownego wykonania wiele funkcji, które zostały już rozwiązane za pomocą, Seler , Apache Spark i Hadoop , Zookeeper , etcd , Cassandra i tak dalej. Wszystkie mają moduły umożliwiające dostęp do swoich usług w języku Python.
[Aktualizacja: Kilka zasobów do rozważenia, jeśli rozważasz Python do intensywnego obliczania w systemach rozproszonych: IPython Parallel i PySpark . Chociaż są to rozproszone systemy obliczeniowe ogólnego przeznaczenia, są one szczególnie łatwo dostępnymi i popularnymi podsystemami nauki i analizy danych].
Wniosek
Masz do dyspozycji gamę alternatyw przetwarzania dla Pythona, od jednowątkowych, z prostymi synchronicznymi wywołaniami do podprocesów, pulami odpytywanych podprocesów, wielowątkową i wielowątkową, kooperatywną wielozadaniowością sterowaną zdarzeniami, aż po przetwarzanie rozproszone.