Python'da alt işlem, çoklu işlem ve iş parçacığı arasında karar vermek?


110

Python programımı, üzerinde çalıştığı makinede birden çok işlemciyi kullanabilmesi için paralel hale getirmek istiyorum. Paralelleştirmem çok basittir, çünkü programın tüm paralel "dizileri" bağımsızdır ve çıktılarını ayrı dosyalara yazar. Bilgi alışverişi için iş parçacığına ihtiyacım yok, ancak iş parçacıklarının ne zaman bittiğini bilmem zorunludur çünkü ardışık düzenimin bazı adımları çıktılarına bağlı.

Taşınabilirlik önemlidir, çünkü bunun Mac, Linux ve Windows üzerindeki herhangi bir Python sürümünde çalışmasını istiyorum. Bu kısıtlamalar göz önüne alındığında, bunu uygulamak için en uygun Python modülü hangisidir? İş parçacığı, alt işlem ve çoklu işlem arasında karar vermeye çalışıyorum, bunların hepsi ilgili işlevselliği sağlıyor gibi görünüyor.

Bununla ilgili herhangi bir fikrin var mı? Taşınabilir olan en basit çözümü istiyorum.


İlgili: stackoverflow.com/questions/1743293/... (ipler saf Python kodu için olmayan bir marş neden görmek için cevabım var okuyun)

1
"Herhangi bir Python sürümü" FAR çok belirsizdir. Python 2.3? 1.x? 3.x? Bu, tatmin edilmesi imkansız bir koşuldur.
detly

Yanıtlar:


64

multiprocessingharika bir İsviçre çakısı tipidir. Uzaktan hesaplamalar bile yapabileceğiniz için iş parçacıklarından daha geneldir. Bu nedenle kullanmanızı önereceğim modül budur.

subprocessModül ayrıca çoklu süreçleri başlatmak için izin verecek, ama yeni çoklu işlem modülüne daha kullanmak daha az elverişli olduğu tespit.

İplikler herkesin bildiği gibi inceliklidir ve CPython ile genellikle bir çekirdekle sınırlandırılırsınız (yorumlardan birinde belirtildiği gibi, Global Yorumlayıcı Kilidi (GIL) Python kodundan adlandırılan C kodunda serbest bırakılabilir) .

Alıntı yaptığınız üç modülün işlevlerinin çoğunun platformdan bağımsız bir şekilde kullanılabileceğine inanıyorum. Taşınabilirlik tarafında, multiprocessingyalnızca Python 2.6'dan beri standart olarak geldiğine dikkat edin (Python'un bazı eski sürümleri için bir sürüm olsa da var). Ama bu harika bir modül!


1
bir atama için, "çoklu işlem" modülünü ve onun pool.map () yöntemini kullandım. kekin parçası !
kmonsoor

Kereviz gibi bir şey de düşünülüyor mu? Neden öyle mi değil mi?
user3245268

Bildiğim kadarıyla, Celery'in daha fazla işin içine girdiğini (bir miktar mesaj komisyoncusu kurmanız gerekir), ancak eldeki soruna bağlı olarak muhtemelen dikkate alınması gereken bir seçenek.
Eric O Lebigot

186

Benim için bu aslında oldukça basit:

Altişlem seçeneği:

subprocessolan diğer yürütülebilir çalıştırmak için etrafında temelde bir sarıcı var --- os.fork()ve os.execve()opsiyonel sıhhi tesisat için bazı desteği ile (ve subprocesses gelen boruları kurma. Açıkçası olabilir diğer bir işlemler arası iletişim (IPC) yuva gibi mekanizmalar, ya da Posix'e veya SysV paylaşılan bellek Ancak, aradığınız programlar tarafından desteklenen arabirimler ve IPC kanalları ile sınırlı kalacaksınız.

Genelde, herhangi bir subprocesseşzamanlı olarak kullanılır - sadece bir dış yardımcı programı çağırır ve çıktısını geri okur veya tamamlanmasını bekler (belki sonuçlarını geçici bir dosyadan okur veya bir veritabanına gönderdikten sonra).

Bununla birlikte, yüzlerce alt işlem üretebilir ve bunları sorgulayabilir. Kendi kişisel favori yardımcı sınıfım tam olarak bunu yapıyor. Modülün en büyük dezavantajı , subprocessI / O desteğinin genel olarak bloke olmasıdır. Python 3.x'in gelecekteki bazı sürümlerinde bunu düzeltmek için bir taslak PEP-3145 ve alternatif bir asyncproc (Uyarı, herhangi bir dokümantasyona veya README'ye değil, doğrudan indirmeye götürür) vardır. Ayrıca fcntl, PopenPIPE dosya tanımlayıcılarınızı doğrudan içe aktarmanın ve değiştirmenin nispeten kolay olduğunu buldum - ancak bunun UNIX olmayan platformlar için taşınabilir olup olmadığını bilmiyorum.

(Güncelleme: 7 Ağustos 2019: ayncio alt işlemleri için Python 3 desteği: asyncio Alt İşlemleri )

subprocess destek işleme neredeyse hiçbir olay vardır ... gerçi kullanabileceğiniz signalmodülü ve düz eski okul UNIX / Linux sinyalleri --- sanki, usulca senin süreçleri öldürerek.

Çoklu işlem seçeneği:

multiprocessingolan mevcut (Python) kodundaki fonksiyonlarını çalıştırmak için işlemlerin bu ailenin arasında daha esnek haberleşme desteği ile. Özellikle, multiprocessingIPC'nizi Queuemümkün olduğunca modülün nesneleri etrafında inşa etmek en iyisidir , ancak Eventnesneleri ve çeşitli diğer özellikleri de kullanabilirsiniz (bunların bazıları, muhtemelen, mmapdesteğin yeterli olduğu platformlarda destek etrafında inşa edilmiştir ).

Python'un multiprocessingmodülü, CPython'un GIL'e (Global Yorumlayıcı Kilidi) rağmen işlemlerinizi birden çok CPU / çekirdek arasında ölçeklendirmesine izin verirken çok benzer arayüzler ve özellikler sağlamayı amaçlamaktadır threading. İşletim sistemi çekirdeğinizin geliştiricileri tarafından yapılan tüm ince ayarlı SMP kilitleme ve tutarlılık çabasından yararlanır.

Parçacığı seçeneği:

threadingolduğu uygulamalar oldukça dar bir aralığı için I / O bağlanmış ve son derece düşük gecikme ile ilgili olan yarar ve (ortak çekirdek bellek) iplik geçiş yükü anahtarlama genel işlem (çoklu CPU çekirdeğinde ölçekte gerekmez) / bağlam değiştirme. Linux'ta bu neredeyse boş bir kümedir (Linux işlem değiştirme süreleri iş parçacığı anahtarlarına oldukça yakındır).

threadingdan uğrar Python iki büyük dezavantajları .

Elbette bunlardan biri uygulamaya özgüdür - çoğunlukla CPython'u etkiler. Bu GIL'dir. Çoğunlukla, çoğu CPython programı ikiden fazla CPU'nun (çekirdek) kullanılabilirliğinden yararlanmayacak ve performans genellikle GIL kilitleme çekişmesinden zarar görecektir .

Uygulamaya özel olmayan daha büyük sorun, evrelerin aynı belleği, sinyal işleyicileri, dosya tanımlayıcılarını ve belirli diğer işletim sistemi kaynaklarını paylaşmasıdır. Bu nedenle programcı, nesne kilitleme, istisna işleme ve kodunun diğer yönleri konusunda son derece dikkatli olmalıdır; bunlar hem incelikli hem de tüm süreci (iş parçacıkları grubu) öldürebilir, durdurabilir veya kilitleyebilir.

Karşılaştırıldığında, multiprocessingmodel her işleme kendi belleğini, dosya tanımlayıcılarını, vb. Verir. Herhangi birindeki bir çökme veya işlenmemiş istisna, yalnızca o kaynağı öldürecektir ve bir çocuğun veya kardeş sürecinin ortadan kaybolmasının sağlam bir şekilde ele alınması, hata ayıklama, izole etmekten çok daha kolay olabilir. ve ileti dizilerindeki benzer sorunları düzeltmek veya çözmek.

  • (Not: NumPythreading gibi büyük Python sistemlerinde kullanım, GIL çekişmesinden, kendi Python kodunuzun çoğundan daha az zarar görebilir. Bunun nedeni, özellikle bunu yapmak için tasarlanmış olmalarıdır; NumPy'nin yerel / ikili bölümleri, örneğin, güvenli olduğunda GIL'i serbest bırakır).

Bükülmüş seçeneği:

Twisted'ın hem zarif hem de anlaşılması çok zor olan başka bir alternatif sunduğunu da belirtmek gerekir . Temel olarak, Twisted hayranlarının dirgenler ve meşaleler ile evime saldırabileceği noktaya kadar aşırı basitleştirme riskine rağmen, Twisted herhangi bir (tek) süreçte olay odaklı işbirliğine dayalı çoklu görev sağlar.

Bunun nasıl mümkün olduğunu anlamak için (select () veya anket () veya benzer işletim sistemi sistem çağrıları select()etrafında oluşturulabilen ) özellikleri hakkında bilgi okunmalıdır . Temel olarak, işletim sisteminin bir dosya tanımlayıcı listesindeki herhangi bir etkinliği veya bazı zaman aşımını beklerken uyku talebinde bulunma yeteneği tarafından yönlendirilir.

Bu çağrıların her birinden uyanmak select()bir olaydır - ya bir dizi soket ya da dosya tanımlayıcıda mevcut (okunabilir) girişleri içerir ya da bazı diğer (yazılabilir) tanımlayıcılarda veya soketlerde kullanılabilir hale gelen arabelleğe alma alanı, bazı istisnai koşullar (TCP bant dışı PUSH'd paketler, örneğin) veya bir TIMEOUT.

Böylelikle Twisted programlama modeli, bu olayların işlenmesi ve ardından ortaya çıkan "ana" işleyicide döngü oluşturarak olayları işleyicilerinize göndermesine izin vererek inşa edilmiştir.

Ben şahsen , programlama modelini çağrıştıran Twisted ismini düşünüyorum ... çünkü soruna yaklaşımınız bir bakıma "çarpık" olmalı. Programınızı girdi verileri ve çıktıları veya sonuçları üzerinde bir dizi işlem olarak düşünmek yerine, programınızı bir hizmet veya arka plan programı olarak yazıyor ve çeşitli olaylara nasıl tepki vereceğini tanımlıyorsunuz. (Aslında bir Twisted programın temel "ana döngüsü" (genellikle? Her zaman?) A reactor()) ' dır .

Twisted'ı kullanmanın en büyük zorlukları , zihninizi olaya dayalı model etrafında döndürmeyi ve ayrıca Twisted çerçevesinde işbirliği yapmak için yazılmayan herhangi bir sınıf kitaplığı veya araç setinin kullanımından kaçınmayı içerir. Bu nedenle Twisted, SSH protokolü işleme, curses için kendi modüllerini ve kendi alt işlem / Popen işlevlerini ve ilk bakışta Python standart kitaplıklarındaki şeyleri kopyalıyor gibi görünen diğer birçok modül ve protokol işleyicisini sağlar.

Hiç kullanmak istemeseniz bile Twisted'ı kavramsal düzeyde anlamanın faydalı olacağını düşünüyorum. İş parçacığı oluşturma, çoklu işleme ve hatta alt süreç işlemenin yanı sıra üstlendiğiniz herhangi bir dağıtılmış işlemde performans, çekişme ve olay işleme hakkında içgörüler sağlayabilir.

( Not: Python 3.x'in daha yeni sürümleri, async def , @ async.coroutine dekoratörü ve await anahtar sözcüğü gibi asyncio (asynchronous I / O) özellikleri ve gelecekteki destekten getiri içerir. Bunların tümü kabaca benzerdir. Bir süreç (kooperatif çoklu görev) perspektifinden bükülmüş ). (Python 3 için Twisted desteğinin mevcut durumu için şu adrese bakın: https://twistedmatrix.com/documents/current/core/howto/python3.html )

Dağıtık seçeneği:

Henüz sormadığınız, ancak dikkate almaya değer başka bir işleme alanı, dağıtılmış işlemedir. Dağıtılmış işleme ve paralel hesaplama için birçok Python aracı ve çerçevesi vardır. Kişisel olarak, kullanımı en kolay olanın o alanda en az sıklıkla kabul edilen şey olduğunu düşünüyorum.

Redis çevresinde dağıtılmış işlem oluşturmak neredeyse önemsizdir . Tüm anahtar deposu çalışma birimlerini ve sonuçları depolamak için kullanılabilir, Redis LIST'leri Queue()benzer nesne olarak kullanılabilir ve PUB / SUB desteği benzer işlemler için kullanılabilir Event. Çalışanlarınızı koordine etmek için tek bir örneğin kapasitesinin ötesinde ölçeklendirme için tutarlı karma ve yük devretme sağlamak üzere topolojiyi ve karma belirteç eşlemelerini depolamak için gevşek bir Redis örnekleri kümesinde çoğaltılan anahtarlarınıza hash uygulayabilir ve değerleri kullanabilirsiniz. ve aralarında veri toplama (turşu, JSON, BSON veya YAML).

Elbette, Redis çevresinde daha büyük ölçekli ve daha sofistike bir çözüm oluşturmaya başladığınızda, Celery , Apache Spark ve Hadoop , Zookeeper , vb. , Cassandra vb. Kullanılarak çözülmüş birçok özelliği yeniden uyguluyorsunuz . Bunların hepsinin hizmetlerine Python erişimi için modülleri vardır.

[Güncelleme: Python'u dağıtık sistemler arasında hesaplama açısından yoğun olarak düşünüyorsanız dikkate almanız gereken birkaç kaynak: IPython Parallel ve PySpark . Bunlar genel amaçlı dağıtılmış bilgi işlem sistemleri olsa da, özellikle erişilebilir ve popüler alt sistemler veri bilimi ve analitiğidir].

Sonuç

Python için tek iş parçacıklı, basit eşzamanlı çağrılardan alt işlemlere, yoklamalı alt işlem havuzlarına, iş parçacığı ve çoklu işlemeye, olay odaklı işbirliğine dayalı çoklu görevlere ve dağıtılmış işlemeye kadar işlem alternatifleri yelpazesine sahipsiniz.


1
Yine de sınıflar / OOP ile çoklu işlemeyi kullanmak zordur.
Tjorriemorrie

2
@Tjorriemorrie: Diğer süreçlerde olabilecek nesnelerin örneklerine yöntem çağrıları göndermenin zor olduğunu tahmin edeceğim. Bunun, ipliklerdeki sorunla aynı olduğunu, ancak daha kolay görünür olduğunu (kırılgan ve belirsiz yarış koşullarına tabi olmak yerine) öneriyorum. Önerilen yaklaşımın, tüm bu tür gönderilerin tek iş parçacıklı, çok iş parçacıklı ve süreçler arasında çalışan Kuyruk nesneleri aracılığıyla gerçekleşmesini sağlamak olacağını düşünüyorum. (Bazı Redis veya Celery Queue uygulamasıyla, bir düğüm kümesinde bile)
Jim Dennis

2
Bu gerçekten iyi bir cevap. Keşke Python3 belgelerinde eşzamanlılığa girişte olsaydı.
root-11

1
@ root-11 bunu belge bakıcılarına önerebilirsiniz; Ücretsiz kullanım için burada yayınladım. Siz ve onlar bunu tamamen veya kısmen kullanabilirsiniz.
Jim Dennis

"Benim için bu aslında oldukça basit:" Bayıldım. çok teşekkürler
jerome

5

Benzer bir durumda, ayrı süreçleri ve ağ soketi aracılığıyla gerekli iletişimin küçük bir kısmını seçtim. Oldukça taşınabilir ve python kullanarak yapması oldukça basit, ancak muhtemelen daha basit değil (benim durumumda başka bir kısıtlamam da vardı: C ++ ile yazılmış diğer işlemlerle iletişim).

Sizin durumunuzda, python iplikleri, en azından CPython kullanırken, gerçek iş parçacıkları olmadığından, muhtemelen çoklu işlem için gidecektim. Pekala, bunlar yerel sistem iş parçacıklarıdır, ancak Python'dan çağrılan C modülleri GIL'i serbest bırakabilir veya bırakmayabilir ve engelleme kodunu çağırırken diğer iş parçacıklarının çalışmasına izin verebilir.


4

CPython'da birden fazla işlemci kullanmak için tek seçeneğiniz multiprocessingmodüldür. CPython , diğer cpus üzerindeki iş parçacıklarının paralel olarak çalışmasını önleyen iç kısımlarında ( GIL ) bir kilit tutar . multiprocessingModül (gibi yeni süreçler oluşturur subprocess) ve aralarındaki iletişimi yönetir.


5
Bu tam olarak doğru değil, AFAIK, C API'yi kullanarak GIL'i serbest bırakabilirsiniz ve bu tür sınırlamalardan muzdarip olmayan IronPython veya Jython gibi başka Python uygulamaları da vardır. Yine de olumsuz oy vermedim.
Bastien Léonard

1

Kabuk açın ve işlerinizi yapmak için unix'in dışarı çıkmasına izin verin:

alt işlemi sarmak için yinelemeler kullanın ve ardından:

Ted Ziuba'nın sitesinden

INPUTS_FROM_YOU | xargs -n1 -0 -P NUM ./process #SAYI paralel süreçler

VEYA

Gnu Parallel de hizmet verecek

Arka odadaki çocukları çok çekirdekli çalışmanız için gönderirken GIL ile takılırsınız.


6
"Taşınabilirlik önemlidir, çünkü bunun Mac, Linux ve Windows üzerindeki herhangi bir Python sürümünde çalışmasını istiyorum."
detly

Bu çözümle, işle tekrar tekrar etkileşime girebilir misiniz? Bunu çoklu işlemede yapabilirsiniz, ancak alt işlemde öyle düşünmüyorum.
abalter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.