Ne Giulio Franco diyor çoklu işlem vs çoklu kullanım için geçerlidir genelde .
Ancak, Python * ek bir sorun vardır: Aynı işlemdeki iki iş parçacığının aynı anda Python kodunu çalıştırmasını engelleyen bir Global Tercüman Kilidi vardır. Bu, 8 çekirdeğiniz varsa ve kodunuzu 8 iş parçacığı kullanacak şekilde değiştirirseniz,% 800 CPU kullanamaz ve 8 kat daha hızlı çalışamaz; aynı% 100 CPU'yu kullanır ve aynı hızda çalışır. (Gerçekte, biraz daha yavaş çalışacaktır, çünkü paylaşılan verileriniz olmasa bile, iş parçacığından ekstra ek yük var, ancak şimdilik bunu görmezden gelin.)
Bunun istisnaları var. Kodunuzun ağır hesaplaması aslında Python'da olmasa da, numpy uygulaması gibi uygun GIL işlemeyi gerçekleştiren özel C koduna sahip bazı kütüphanelerde, diş açmadan beklenen performans avantajını elde edersiniz. Eğer ağır hesaplama çalıştırdığınız ve beklediğiniz bazı alt süreçler tarafından yapılıyorsa aynı şey geçerlidir.
Daha da önemlisi, bunun önemli olmadığı durumlar vardır. Örneğin, bir ağ sunucusu zamanının çoğunu paketleri okuma ağ üzerinden geçirir ve bir GUI uygulaması zamanının çoğunu kullanıcı olaylarını beklerken geçirir. Bir ağ sunucusunda veya GUI uygulamasında iş parçacığı kullanmanın bir nedeni, ana iş parçacığının hizmet ağ paketlerine veya GUI olaylarına devam etmesini durdurmadan uzun süren "arka plan görevleri" yapmanıza izin vermektir. Ve bu Python iş parçacıkları ile gayet iyi çalışıyor. (Teknik açıdan bakıldığında, Python iş parçacığı, size çekirdek paralellik vermese de size eşzamanlılık kazandırdığı anlamına gelir.)
Ancak saf Python'da CPU'ya bağlı bir program yazıyorsanız, daha fazla iş parçacığı kullanmak genellikle yardımcı olmaz.
Ayrı işlemlerin GIL ile ilgili herhangi bir sorunu yoktur, çünkü her işlemin kendi ayrı GIL'si vardır. Tabii ki, diğer dillerde olduğu gibi, iş parçacıkları ve süreçler arasında hala aynı dengeye sahipsiniz - süreçler arasında veri paylaşmak iş parçacıkları arasında olduğundan daha zor ve daha pahalıdır, çok sayıda işlem yürütmek veya oluşturmak ve yok etmek pahalı olabilir Ancak GIL, örneğin C veya Java için doğru olmayan bir şekilde, süreçler arasındaki dengeye dayanır. Böylece, Python'da çok işlemciliği C veya Java'da olduğundan çok daha sık kullandığınızı göreceksiniz.
Bu arada, Python'un "piller dahil" felsefesi bazı iyi haberler getiriyor: Tek satırlık bir değişiklikle iş parçacıkları ve işlemler arasında ileri geri değiştirilebilen kod yazmak çok kolay.
Kodunuzu, girdi ve çıktı dışındaki diğer işlerle (veya ana programla) hiçbir şey paylaşmayan bağımsız "işler" olarak tasarlarsanız, kodunuzu aşağıdaki concurrent.futures
gibi bir iş parçacığı havuzuna yazmak için kitaplığı kullanabilirsiniz :
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
executor.submit(job, argument)
executor.map(some_function, collection_of_independent_things)
# ...
Hatta bu işlerin sonuçlarını alabilir ve başka işlere aktarabilir, idam sırasına veya tamamlanma sırasına, vb. Bekleyebilirsiniz; Future
ayrıntılar için nesneler bölümünü okuyun .
Şimdi, programınız sürekli olarak% 100 CPU kullanıyorsa ve daha fazla iş parçacığı eklemek sadece yavaşlatıyorsa, GIL sorunuyla karşılaşıyorsunuz, bu yüzden süreçlere geçmeniz gerekiyor. Tek yapmanız gereken ilk satırı değiştirmek:
with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:
Tek gerçek uyarı, işlerinizin argümanlarının ve dönüş değerlerinin kullanılabilir çapraz işlem olması için seçilebilir (ve turşu için çok fazla zaman veya bellek almaması) gerektiğidir. Genellikle bu bir problem değildir, ama bazen de olabilir.
Peki ya işleriniz kendi kendine yetmezse? Kodunuzu, iletileri birinden diğerine geçiren işler açısından tasarlayabiliyorsanız, yine de oldukça kolaydır. Havuzları kullanmak yerine threading.Thread
ya da onlara multiprocessing.Process
güvenmek zorunda kalabilirsiniz . Ve açıkça queue.Queue
veya multiprocessing.Queue
nesneler oluşturmanız gerekecek . (Çok sayıda başka seçenek vardır - borular, soketler, sürülere sahip dosyalar… ama asıl nokta, bir Yöneticinin otomatik büyüsü yetersizse manuel olarak bir şeyler yapmanız gerekir .)
Peki ya mesaj geçişine bile güvenemezseniz? Aynı yapıyı değiştirmek ve birbirlerinin değişikliklerini görmek için iki işe ihtiyacınız varsa ne olur? Bu durumda, el ile eşitleme (kilitler, semaforlar, koşullar vb.) Yapmanız ve işlemleri kullanmak istiyorsanız, önyükleme yapmak için açık paylaşılan bellek nesneleri kullanmanız gerekir. Bu, çoklu iş parçacığının (veya çoklu işlemin) güçleştiği zamandır. Bundan kaçınabiliyorsanız, harika; Eğer yapamazsanız, bir kişinin SO cevabına koyabileceğinden daha fazlasını okumalısınız.
Bir yorumdan, Python'daki iş parçacıkları ve işlemler arasında neyin farklı olduğunu bilmek istediniz. Gerçekten, Giulio Franco'nun cevabını ve benimkini ve tüm bağlantılarımızı okursanız, bu her şeyi kapsamalıdır… ama bir özet kesinlikle yararlı olacaktır, işte burada:
- Konular varsayılan olarak veri paylaşır; süreçler değil.
- (1) 'in bir sonucu olarak, süreçler arasında veri göndermek genellikle veriyi temizlemeyi ve seçimini kaldırmayı gerektirir. **
- (1) 'in bir başka sonucu olarak, doğrudan süreçler arasında veri paylaşımı genellikle verilerin Değer, Dizi ve
ctypes
türler gibi düşük düzeyli biçimlere yerleştirilmesini gerektirir .
- İşlemler GIL'e tabi değildir.
- Bazı platformlarda (özellikle Windows), süreçlerin oluşturulması ve yok edilmesi çok daha pahalıdır.
- Süreçlerde, bazıları farklı platformlarda farklı olan bazı ekstra kısıtlamalar vardır. Ayrıntılar için Programlama yönergelerine bakın.
threading
Modül bazı özellikleri yoktur multiprocessing
modülü. ( multiprocessing.dummy
Eksik API'nin çoğunu iş parçacıklarının üstüne almak için kullanabilirsiniz veya concurrent.futures
bunun gibi üst düzey modüller kullanabilirsiniz ve endişelenmeyin.)
* Aslında bu sorunu yaşayan Python değil, CPython, o dilin "standart" uygulamasıdır. Jython gibi diğer bazı uygulamalarda GIL yoktur.
** Çatal başlatma yöntemini, Windows olmayan platformların çoğunda yapabileceğiniz çoklu işlem için kullanıyorsanız, her alt işlem, ebeveyn başlatıldığında çocuk başlatıldığında sahip olduğu kaynakları alır; bu, verileri çocuklara iletmenin başka bir yolu olabilir.
Thread
modül de var (_thread
python 3.x'de denir ). Dürüst olmak gerekirse, farkları kendim hiç anlamadım ...