Bir PyQt uygulamasında iş parçacığı oluşturma: Qt iş parçacığı veya Python iş parçacığı mı kullanıyorsunuz?


118

Verileri web bağlantısı üzerinden düzenli olarak alan bir GUI uygulaması yazıyorum. Bu alım biraz zaman aldığından, bu, geri alma işlemi sırasında kullanıcı arayüzünün yanıt vermemesine neden olur (daha küçük parçalara bölünemez). Bu nedenle, web bağlantısını ayrı bir çalışan iş parçacığına dış kaynak olarak kullanmak istiyorum.

[Evet, biliyorum, şimdi iki sorunum var .]

Her neyse, uygulama PyQt4 kullanıyor, bu yüzden daha iyi seçeneğin ne olduğunu bilmek istiyorum: Qt'nin iş parçacığını mı yoksa Python threadingmodülünü mü kullanmak ? Her birinin avantajları / dezavantajları nelerdir? Yoksa tamamen farklı bir öneriniz mi var?

Düzenleme (yeniden ödül): Benim özel durumumdaki çözüm muhtemelen Jeff Ober ve Lukáš Lalinský'nin önerdiği gibi engellemeyen bir ağ isteği kullanacak olsa da (temelde eşzamanlılık sorunlarını ağ uygulamasına bırakmak), yine de daha fazlasını istiyorum genel soruya derinlemesine cevap:

PyQt4'leri (yani Qt'leri) yerel Python iş parçacıkları ( threadingmodülden) yerine kullanmanın avantajları ve dezavantajları nelerdir ?


Düzenleme 2: Cevaplarınız için hepinize teşekkürler. % 100 anlaşma olmamasına rağmen, cevabın "Qt kullan" olduğu konusunda yaygın bir fikir birliği var gibi görünüyor, çünkü bunun avantajı kütüphanenin geri kalanıyla entegrasyon olmasına rağmen gerçek dezavantajlara neden olmuyor.

İki iş parçacığı uygulaması arasında seçim yapmak isteyenler için, abbot'un bağlantı verdiği PyQt posta listesi dizisi de dahil olmak üzere burada verilen tüm yanıtları okumalarını şiddetle tavsiye ederim .

Ödül için düşündüğüm birkaç cevap vardı; sonunda, çok ilgili harici referans için başrahipleri seçtim; ancak yakın bir görüşmeydi.

Tekrar teşekkürler.

Yanıtlar:


107

Bu çok uzun zaman önce PyQt posta listesinde tartışılmıştı . Giovanni Bajo'nun konuyla ilgili yorumlarını aktarırken :

Çoğunlukla aynı. Temel fark, QThreads'in Qt (eşzamansız sinyaller / yuvalar, olay döngüsü vb.) İle daha iyi entegre edilmesidir. Ayrıca, Qt'yi bir Python iş parçacığından kullanamazsınız (örneğin QApplication.postEvent aracılığıyla ana iş parçacığına olay gönderemezsiniz): bunun çalışması için bir QThread'e ihtiyacınız vardır.

Genel bir kural, Qt ile bir şekilde etkileşime girecekseniz QThreads kullanmak ve başka türlü Python thread'lerini kullanmak olabilir.

Ve PyQt'un yazarından bu konu hakkında daha önceki bazı yorumlar: "her ikisi de aynı yerel iş parçacığı uygulamaları etrafında sarmalayıcılardır". Ve her iki uygulama da GIL'i aynı şekilde kullanır.


2
Güzel cevap, ama bence nerede özetlemediğinizi değil, posta listesinden Giovanni
Bajo'dan

2
Neden QApplication.postEvent () aracılığıyla ana iş parçacığına olay gönderemediğinizi ve bunun için bir QThread'e ihtiyacınız olduğunu merak ediyorum? Sanırım bunu yapan insanlar gördüm ve işe yaradı.
Trilarion

1
QCoreApplication.postEventPlatformlar arası çalışan ve 1000 saat boyunca test edilmiş bir uygulamada, saniyede 100 kez bir Python iş parçacığından aradım . Bundan hiç sorun görmedim. Hedef nesne MainThread veya QThread içinde bulunduğu sürece sorun olmadığını düşünüyorum. Ayrıca güzel bir kütüphaneye sardım , qtutils'e bakın .
three_pineapples

2
Bu soru ve cevabın yüksek oranda oylanmış doğası göz önüne alındığında, ekhumoro'nun Python ipliklerinden belirli Qt yöntemlerini kullanmanın güvenli olduğu koşulları açıklayan yeni bir SO cevabına işaret etmeye değer olduğunu düşünüyorum . Bu, ben ve @Trilarion tarafından görülen gözlemlenen davranışla örtüşüyor.
three_pineapples

33

Python'un konuları daha basit ve daha güvenli olacak ve I / O tabanlı bir uygulama için olduğu için GIL'i atlayabilecekler. Bununla birlikte, Twisted veya non-blocking sockets / select kullanarak bloke edilmeyen I / O'ları düşündünüz mü?

DÜZENLEME: konular hakkında daha fazlası

Python konuları

Python'un konuları sistem iplikleridir. Ancak Python, yorumlayıcının bir seferde yalnızca belirli boyutta bayt kodu komutları yürütmesini sağlamak için global bir yorumlayıcı kilidi (GIL) kullanır. Neyse ki Python, giriş / çıkış işlemleri sırasında GIL'i serbest bırakır ve bu da iş parçacıklarını engellemeyen G / Ç'yi simüle etmek için kullanışlı hale getirir.

Önemli uyarı: bayt kod komut sayısı yok, bu durum yanıltıcı olabilir değil bir programda satır sayısına karşılık gelmektedir. Python'da tek bir atama bile atomik olmayabilir, bu nedenle GIL ile bile atomik olarak çalıştırılması gereken herhangi bir kod bloğu için bir muteks kilidi gereklidir .

QT konuları

Python, kontrolü üçüncü taraf derlenmiş bir modüle verdiğinde, GIL'i serbest bırakır. Gerektiğinde atomikliği sağlamak modülün sorumluluğu haline gelir. Kontrol geri verildiğinde, Python GIL'i kullanacaktır. Bu, iş parçacıklarıyla birlikte 3. parti kitaplıkların kullanımını kafa karıştırıcı hale getirebilir. Harici bir iş parçacığı kitaplığı kullanmak daha da zordur çünkü kontrolün nerede ve ne zaman modülün ellerinde tercümana göre olduğu konusunda belirsizlik ekler.

QT iş parçacıkları, GIL serbest bırakıldığında çalışır. QT iş parçacıkları, QT kitaplık kodunu (ve GIL'yi edinmeyen diğer derlenmiş modül kodunu) eşzamanlı olarak yürütebilir. Ancak, bir QT iş parçacığı bağlamında yürütülen Python kodu hala GIL'i alır ve şimdi kodunuzu kilitlemek için iki mantık setini yönetmeniz gerekir.

Sonunda, hem QT iş parçacıkları hem de Python iş parçacıkları, sistem iş parçacıkları etrafında sarmalayıcılardır. Python iş parçacığı kullanımı marjinal olarak daha güvenlidir, çünkü Python'da yazılmayan parçalar (örtük olarak GIL kullanılarak) GIL'i her durumda kullanır (yine de yukarıdaki uyarı hala geçerlidir).

Engellemesiz G / Ç

Dişler, uygulamanıza olağanüstü karmaşıklık katar. Özellikle Python yorumlayıcısı ile derlenmiş modül kodu arasındaki zaten karmaşık etkileşimle uğraşırken. Birçoğu olay tabanlı programlamayı takip etmenin zor olduğunu düşünürken, olay tabanlı, engellemesiz G / Ç, genellikle iş parçacıklarından çok daha az mantıklıdır.

Eşzamansız G / Ç ile, her açık tanımlayıcı için yürütme yolunun tutarlı ve düzenli olduğundan her zaman emin olabilirsiniz. Açıkçası, kod bir açık kanala bağlı olduğunda ne yapılması gerektiği gibi, başka bir açık kanal veri döndürdüğünde çağrılacak olan kodun sonuçlarına bağlı olduğu gibi ele alınması gereken konular vardır.

Olay tabanlı, engellemesiz G / Ç için güzel bir çözüm, yeni Diesel kitaplığıdır. Şu anda Linux ile sınırlı, ancak olağanüstü derecede hızlı ve oldukça zarif.

Sisteminiz için mevcut en hızlı yöntemi kullanarak (derleme zamanında belirlenir) olay tabanlı programlama için temel bir çerçeve sağlayan harika libevent kitaplığı etrafında bir sarmalayıcı olan pyevent'i öğrenmek için de zaman ayırmaya değer .


Re Twisted vs .: Gerçek ağ oluşturma işini yapan bir üçüncü taraf kitaplığı kullanıyorum; Etrafına yamamaktan kaçınmak isterim. Ama yine de buna bakacağım, teşekkürler.
balpha

2
Aslında hiçbir şey GIL'i geçemez. Ancak Python, G / Ç işlemleri sırasında GIL'i serbest bırakır. Python ayrıca, GIL'i kendileri almaktan / serbest bırakmaktan sorumlu olan derlenmiş modüllere 'devrederken' GIL'i serbest bırakır.
Jeff Ober

2
Güncelleme çok yanlış. Python kodu, bir Python thread'inde QThread'dekiyle tamamen aynı şekilde çalışır. Python kodunu çalıştırdığınızda (ve ardından Python iş parçacıkları arasındaki yürütmeyi yönettiğinde) GIL'i edinirsiniz, C ++ kodunu çalıştırdığınızda onu serbest bırakırsınız. Hiç fark yok.
Lukáš Lalinský

1
Hayır, mesele şu ki, iş parçacığını nasıl yaratırsanız oluşturun, Python yorumlayıcısı umursamıyor. Tek umursadığı GIL'i alabilmesi ve X talimatından sonra onu serbest bırakabilmesi / yeniden alabilmesidir. Örneğin, ayrı bir iş parçacığında çağrılacak olan bir C kitaplığından bir geri arama oluşturmak için ctypes kullanabilirsiniz ve kod, farklı bir iş parçacığı olduğunu bile bilmeden iyi çalışacaktır. İplik modülüyle ilgili gerçekten özel bir şey yok.
Lukáš Lalinský

1
QThread'in kilitleme konusunda ne kadar farklı olduğunu ve "kodunuzu kilitlemek için iki mantık setini yönetmeniz gerektiğini" söylüyordunuz. Demek istediğim, hiç de farklı olmadığı. İş parçacığını başlatmak için ctypes ve pthread_create kullanabilirim ve tamamen aynı şekilde çalışacaktır. Python kodunun GIL'i önemsemesi gerekmez.
Lukáš Lalinský

21

Avantajı QThread, Qt kitaplığının geri kalanıyla entegre olmasıdır. Yani, Qt'deki iş parçacığına duyarlı yöntemlerin hangi evrede çalıştıklarını bilmeleri ve nesneleri evreler arasında hareket ettirmek için kullanmanız gerekecek QThread. Diğer bir kullanışlı özellik, bir iş parçacığında kendi olay döngünüzü çalıştırmaktır.

Bir HTTP sunucusuna erişiyorsanız, dikkate almalısınız QNetworkAccessManager.


1
Jeff Ober'in cevabına yorumladığımın dışında QNetworkAccessManagerumut verici görünüyor. Teşekkürler.
balpha

14

PyTalk'ta çalışırken kendime aynı soruyu sordum .

Qt QThreadkullanıyorsanız, Qt çerçevesini ve özellikle sinyal / slot sistemini kullanabilmek için kullanmanız gerekir .

Sinyal / slot motoru ile bir iş parçacığından diğerine ve projenizin her bölümüyle konuşabileceksiniz.

Dahası, her ikisi de bir C ++ bağlama olduğundan, bu seçim hakkında çok fazla performans sorusu yoktur.

İşte PyQt ve thread deneyimim.

Kullanmanızı tavsiye ederim QThread.


9

Jeff'in bazı iyi noktaları var. Herhangi bir GUI güncellemesini yalnızca bir ana iş parçacığı yapabilir. GUI'yi iş parçacığı içinden güncellemeniz gerekiyorsa, Qt-4'ün sıraya alınmış bağlantı sinyalleri , iş parçacıkları arasında veri göndermeyi kolaylaştırır ve QThread kullanıyorsanız otomatik olarak çağrılır; Parametre eklemek kolay olsa da, Python thread kullanıyorsan öyle olacaklarından emin değilim connect().


5

Ben de gerçekten tavsiye edemem ama CPython ve Qt iş parçacıkları arasındaki farkları açıklamayı deneyebilirim.

Her şeyden önce, CPython iş parçacıkları eşzamanlı olarak çalışmaz, en azından Python kodu çalışmaz. Evet, her Python iş parçacığı için sistem iş parçacığı oluştururlar, ancak yalnızca şu anda Global Yorumlayıcı Kilidi bulunduran iş parçacığının çalışmasına izin verilir (C uzantıları ve FFI kodu bunu atlayabilir, ancak iş parçacığı GIL'i tutmazken Python bayt kodu yürütülmez).

Öte yandan, temelde sistem iş parçacıkları üzerinde ortak bir katman olan, Global Yorumlayıcı Kilidine sahip olmayan ve bu nedenle aynı anda çalışabilen Qt iş parçacıklarına sahibiz. PyQt'un bununla nasıl başa çıktığından emin değilim, ancak Qt iş parçacıklarınız Python kodunu çağırmadıkça, eşzamanlı olarak çalışabilmelidir (çeşitli yapılarda uygulanabilecek çeşitli ekstra kilitler bar).

Ekstra ince ayar için, GIL'in sahipliğini değiştirmeden önce yorumlanan bayt kodu talimatlarının miktarını değiştirebilirsiniz - daha düşük değerler, daha fazla bağlam değiştirme (ve muhtemelen daha yüksek yanıt verme) anlamına gelir, ancak tek tek iş parçacığı başına daha düşük performans (bağlam anahtarlarının maliyetleri vardır - eğer siz Her birkaç talimatı değiştirmeyi deneyin, bu hıza yardımcı olmaz)

Umarım sorunlarınıza yardımcı olur :)


7
Burada not etmek önemlidir: PyQt QThreads , Global Tercüman Kilidini alır . Tüm Python kodu GIL'i kilitler ve PyQt'da çalıştırdığınız herhangi bir QThreads Python kodunu çalıştıracaktır. (Değilse, aslında PyQt'un "Py" kısmını kullanmıyorsunuz :). Bu Python kodunu harici bir C kitaplığına ertelemeyi seçerseniz, GIL serbest bırakılacaktır, ancak bu, bir Python dizisi veya bir Qt dizisi kullanmanızdan bağımsız olarak geçerlidir.
quark

Aslında iletmeye çalıştığım şey buydu, tüm Python kodu kilidi alıyor, ancak C / C ++ kodunun ayrı iş parçacığında çalışması önemli değil
p_l

0

Ben Python ve PyQt konuları arasında kesin farklar hakkında açıklama yapamam ama kullandığınız yapmaya çalıştığınızı şeyi yapıyorum QThread, QNetworkAcessManagerve çağrı emin olarak QApplication.processEvents()iplik hayatta iken. GUI duyarlılığı gerçekten çözmeye çalıştığınız sorunsa, daha sonra yardımcı olacaktır.


1
QNetworkAcessManageriş parçacığı veya processEvents. Eşzamansız GÇ işlemlerini kullanır.
Lukáš Lalinský

Oops ... evet, QNetworkAcessManagerve kombinasyonunu kullanıyorum httplib2. Eşzamansız kodum kullanır httplib2.
brianz
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.