İş parçacıkları Python'da nasıl çalışır ve Python ile iş parçacığı oluşturmaya özgü yaygın tuzaklar nelerdir?


85

Python'da iş parçacıklarının nasıl çalıştığı konusunda kafamı toplamaya çalışıyorum ve nasıl çalıştıkları hakkında iyi bilgi bulmak zor. Belki bir bağlantı ya da başka bir şey eksik olabilir, ancak görünüşe göre resmi belgeler konu hakkında çok kapsamlı değil ve iyi bir yazı bulamadım.

Anladığım kadarıyla, aynı anda yalnızca bir iş parçacığı çalışıyor olabilir ve etkin iş parçacığı her 10 yönergede bir değişir mi?

Nerede iyi bir açıklama var veya bir açıklama yapabilir misiniz? Python ile iş parçacığı kullanırken karşılaştığınız yaygın sorunların farkında olmak da çok güzel olurdu.

Yanıtlar:


51

Evet, Global Yorumlayıcı Kilidi (GIL) nedeniyle aynı anda yalnızca bir iş parçacığı çalıştırılabilir. İşte bununla ilgili bazı bilgiler içeren bazı bağlantılar:

Son bağlantıdan ilginç bir alıntı:

Tüm bunların ne anlama geldiğini açıklamama izin verin. İş parçacığı aynı sanal makine içinde çalışır ve dolayısıyla aynı fiziksel makine üzerinde çalışır. İşlemler aynı fiziksel makinede veya başka bir fiziksel makinede çalışabilir. Uygulamanızı iş parçacıkları etrafında tasarlarsanız, birden çok makineye erişmek için hiçbir şey yapmamışsınızdır. Böylece, tek makinede olduğu kadar çok çekirdeğe ölçekleyebilirsiniz (ki bu zaman içinde epeyce olacaktır), ancak gerçekten web ölçeklerine ulaşmak için, yine de çoklu makine sorununu çözmeniz gerekir.

Çok çekirdekli kullanmak istiyorsanız, ön işleme, gerçek paralelleştirme yapmak için süreç tabanlı bir API tanımlar. PEP ayrıca bazı ilginç kriterler içermektedir.


1
Smoothspan alıntısıyla ilgili gerçekten bir yorum: kesinlikle Python diş açma, makinede birden fazla çekirdeğe sahip olsa bile sizi etkili bir şekilde tek bir çekirdekle sınırlıyor mu? Bir sonraki iş parçacığı bir bağlam anahtarı olmadan kullanıma hazır olabileceğinden çok çekirdekli avantajlar olabilir, ancak Python iş parçacıklarınız hiçbir zaman bir seferde> 1 çekirdek kullanamaz.
James Brady

2
Doğru, python iplikleri pratik olarak tek çekirdekle sınırlıdır, bir C modülü GIL ile güzel bir şekilde etkileşime girmedikçe ve kendi yerel iş parçacığını çalıştırmaz.
Arafangion

Aslında, birden çok çekirdek, her bir iş parçacığının GIL'e erişip erişemediğini kontrol etmede çok fazla karmaşa olduğundan iş parçacıklarını daha az verimli hale getirir . Yeni GIL ile bile performans hala daha kötü ... dabeaz.com/python/NewGIL.pdf
Temel

2
Lütfen GIL ile ilgili hususların tüm tercümanlar için geçerli olmadığını unutmayın. Bildiğim kadarıyla hem IronPython hem de Jython bir GIL olmadan çalışıyor ve kodlarının çok işlemcili donanımı daha verimli kullanmasına izin veriyor. Arafangion'un da belirttiği gibi, Python veri öğelerine erişim gerektirmeyen kod kilidi serbest bırakır ve daha sonra geri dönmeden önce tekrar alırsa, CPython yorumlayıcısı da düzgün şekilde çok iş parçacıklı çalışabilir.
holdenweb

Python'da iş parçacıkları arasında bağlam geçişine ne sebep olur? Zamanlayıcı kesintilerine dayanıyor mu? Engelleme mi yoksa belirli bir getiri çağrısı mı?
CMCDragonkai

36

Python, girmesi oldukça kolay bir dil, ancak bazı uyarılar var. Bilmeniz gereken en büyük şey Global Tercüman Kilididir. Bu, yorumlayıcıya yalnızca bir iş parçacığının erişmesine izin verir. Bu iki şey anlamına gelir: 1) nadiren kendinizi python'da bir kilit deyimi kullanırken bulursunuz ve 2) çok işlemcili sistemlerden yararlanmak istiyorsanız, ayrı işlemler kullanmanız gerekir. DÜZENLEME: GIL'de de dolaşmak istiyorsanız, kodun bir kısmını C / C ++ 'ya koyabileceğinizi de belirtmeliyim.

Bu nedenle, konuları neden kullanmak istediğinizi yeniden düşünmeniz gerekir. Uygulamanızı çift çekirdekli mimariden yararlanacak şekilde paralel hale getirmek istiyorsanız, uygulamanızı birden çok sürece bölmeyi düşünmeniz gerekir.

Yanıt verebilirliği artırmak istiyorsanız, iş parçacıkları kullanmayı DİKKAT EDİN. Mikro diş açma gibi başka alternatifler de var . Ayrıca incelemeniz gereken bazı çerçeveler vardır:


@JS - Düzeltildi. Bu liste yine de modası geçmişti.
Jason Baker

Çok çekirdekli bir sistemden yararlanmak için birden fazla sürece (tüm ek yük ile birlikte) ihtiyaç duymanız bana yanlış geliyor. 32 mantıksal çekirdeğe sahip bazı sunucularımız var - bu yüzden onları verimli kullanmak için 32 işleme ihtiyacım var? Madness
Temel

@Basic - Bu günlerde bir işlem başlatmaya karşı bir iş parçacığı başlatmanın ek yükü minimumdur. Sanırım saniyede binlerce sorudan bahsediyorsak problemler görmeye başlayabilirsiniz, ancak o zaman ilk etapta böylesine yoğun bir hizmet için Python seçimini sorgulardım.
Jason Baker

20

Aşağıda temel bir diş açma örneği verilmiştir. 20 iş parçacığı üretecek; her iş parçacığı kendi iş parçacığı numarasını verir. Çalıştırın ve yazdırdıkları sıraya dikkat edin.

import threading
class Foo (threading.Thread):
    def __init__(self,x):
        self.__x = x
        threading.Thread.__init__(self)
    def run (self):
          print str(self.__x)

for x in xrange(20):
    Foo(x).start()

Sizin de ima ettiğiniz gibi, Python iş parçacıkları zaman dilimleme yoluyla uygulanır. "Paralel" etkiyi bu şekilde elde ederler.

Örneğimde Foo sınıfım iş parçacığını genişletir, daha sonra runbir iş parçacığında çalıştırmak istediğiniz kodun gittiği yöntemi uyguluyorum . start()İplik nesnesinde çağırdığınız iş parçacığını başlatmak için , bu otomatik olarakrun yöntem ...

Tabii ki, bu sadece temel bilgiler. Sonunda iş parçacığı senkronizasyonu ve mesaj geçişi için semaforlar, muteksler ve kilitler hakkında bilgi edinmek isteyeceksiniz.


10

Bireysel çalışanlar G / Ç bağlı işlemler yapıyorsa python'da iş parçacıkları kullanın. Bir makinede birden çok çekirdek arasında ölçeklendirmeye çalışıyorsanız ya python için iyi bir IPC çerçevesi bulun ya da farklı bir dil seçin.


5

Not: Bahsettiğim her yerde , açıkça belirtilinceye kadar threadözellikle python'daki konuları kastediyorum .

İplikler python'da biraz farklı çalışır. C/C++Arka plandan . Python'da, belirli bir anda yalnızca bir iş parçacığı çalışır durumda olabilir. Bu, python'daki İş parçacığının birden fazla işlem çekirdeğinin gücünden gerçekten yararlanamayacağı anlamına gelir çünkü iş parçacıklarının birden çok çekirdekte paralel olarak çalışması mümkün değildir.

Python'daki bellek yönetimi iş parçacığı güvenli olmadığından, her iş parçacığı python yorumlayıcısındaki veri yapılarına özel erişim gerektirir. Bu özel erişim (global interpretr lock) adlı bir mekanizma tarafından elde edilir .GIL

Why does python use GIL?

Birden çok iş parçacığının aynı anda yorumlayıcı durumuna erişmesini ve yorumlayıcı durumunu bozmasını önlemek için.

Buradaki fikir, bir iş parçacığı çalıştırıldığında (ana iş parçacığı olsa bile) , bir GIL edinilir ve önceden tanımlanmış bir zaman aralığından sonra GIL geçerli iş parçacığı tarafından serbest bırakılır ve başka bir iş parçacığı tarafından yeniden alınır (varsa).

Why not simply remove GIL?

GIL'i kaldırmak imkansız değil, sadece bunu yapma pratiğinde, erişimi serileştirmek için yorumlayıcının içine çoklu kilitler koyuyoruz, bu da tek bir iş parçacıklı uygulamayı daha az performanslı hale getiriyor.

bu nedenle GIL'i kaldırmanın maliyeti, hiçbir zaman arzu edilmeyen tek bir iş parçacıklı uygulamanın düşük performansıyla karşılanır.

So when does thread switching occurs in python?

İş parçacığı anahtarı, GIL serbest bırakıldığında gerçekleşir.Peki GIL Ne Zaman Çıktı? Dikkate alınması gereken iki senaryo var.

Bir İş Parçacığı CPU Bağlı işlemler yapıyorsa (Ex görüntü işleme).

Python'un eski sürümlerinde, Thread anahtarlama, sabit no python talimatlarından sonra gerçekleşiyordu. Varsayılan olarak ayarlandı 100. Tek bir talimatı yürütmek için harcanan zamandan beri geçişin ne zaman gerçekleşmesi gerektiğine karar vermek için çok iyi bir politika olmadığı ortaya çıktı. milisaniyeden bir saniyeye kadar çok çılgınca olabilir. Bu nedenle, GIL'i her 100talimattan sonra, ne kadar sürede olursa olsun yayınlamak kötü bir ilkedir.

Yeni sürümlerde, iş parçacığını değiştirmek için bir ölçü olarak komut sayımı kullanmak yerine, yapılandırılabilir bir zaman aralığı kullanılır. Varsayılan anahtar aralığı 5 milisaniyedir sys.getswitchinterval(). Kullanarak mevcut anahtar aralığını alabilirsiniz . Bu, kullanılarak değiştirilebilirsys.setswitchinterval()

Bir İş Parçacığı bazı GÇ Bağlı İşlemler yapıyorsa (Ex dosya sistemi erişimi veya
ağ GÇ)

İş parçacığı IO işleminin tamamlanmasını beklediğinde GIL serbest bırakılır.

Which thread to switch to next?

Yorumlayıcının kendi zamanlayıcısı yoktur. Aralığın sonunda hangi iş parçacığı programlanırsa işletim sisteminin kararıdır. .


3

GIL için kolay bir çözüm, çoklu işlem modülüdür. Diş açma modülünün yerine bir damla olarak kullanılabilir, ancak iş parçacıkları yerine birden çok Yorumlayıcı işlemi kullanır. Bu nedenle, basit şeyler için düz iş parçacığından biraz daha fazla ek yük vardır, ancak ihtiyacınız olursa size gerçek paralelleştirme avantajı sağlar. Ayrıca, birden fazla fiziksel makineye kolayca ölçeklenebilir.

Gerçekten büyük ölçekli paralelleştirmeye ihtiyacınız varsa, daha ileriye bakacak olursanız, ancak daha kapsamlı bir çerçeve uygulamaya koyacak tüm çalışmalar olmadan yalnızca bir bilgisayarın tüm çekirdeklerine veya birkaç farklı çekirdeğe ölçeklendirmek istiyorsanız, bu sizin için olduğundan .


2

Birden çok görevin görünümünü göstermek için GIL'in sık sık anket yapmaya ayarlandığını hatırlamaya çalışın. Bu ayar ince ayarlanabilir, ancak iş parçacıklarının yaptığı işler olması gerektiği veya birçok bağlam anahtarının sorunlara neden olacağı önerisini sunuyorum.

İşlemciler konusunda birden çok ebeveyn önerecek kadar ileri gider ve aynı çekirdek (ler) üzerinde benzer işler yapmaya çalışırdım.

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.