Piszę aplikację GUI, która regularnie pobiera dane przez połączenie internetowe. Ponieważ pobieranie trwa chwilę, powoduje to, że interfejs użytkownika nie odpowiada podczas procesu pobierania (nie można go podzielić na mniejsze części). Dlatego chciałbym przekazać połączenie internetowe do osobnego wątku roboczego.
[Tak, wiem, teraz mam dwa problemy .]
W każdym razie aplikacja korzysta z PyQt4, więc chciałbym wiedzieć, jaki jest lepszy wybór: użyć wątków Qt czy użyć threading
modułu Python ? Jakie są zalety / wady każdego z nich? A może masz zupełnie inną sugestię?
Edycja (re bounty): Chociaż rozwiązaniem w moim konkretnym przypadku prawdopodobnie będzie użycie nieblokującego żądania sieciowego, takiego jak zasugerowali Jeff Ober i Lukáš Lalinský (więc zasadniczo pozostawiając problemy z współbieżnością implementacji sieciowej), nadal chciałbym uzyskać więcej dogłębna odpowiedź na ogólne pytanie:
Jakie są zalety i wady używania wątków PyQt4 (tj. Qt) w porównaniu z natywnymi wątkami Pythona (z threading
modułu)?
Edycja 2: Dziękuję wszystkim za odpowiedzi. Chociaż nie ma 100% zgodności, wydaje się, że istnieje powszechna zgoda co do tego, że odpowiedzią jest „użyj Qt”, ponieważ zaletą tego jest integracja z resztą biblioteki, która nie powoduje żadnych rzeczywistych wad.
Wszystkim, którzy chcą wybrać jedną z dwóch implementacji wątków, bardzo polecam przeczytanie wszystkich odpowiedzi tutaj udzielonych, w tym wątku listy dyskusyjnej PyQt, do którego opat prowadzi linki.
Było kilka odpowiedzi, które rozważałem w sprawie nagrody; w końcu wybrałem opata jako bardzo istotne odniesienie zewnętrzne; było to jednak mało prawdopodobne.
Dzięki jeszcze raz.
źródło
QCoreApplication.postEvent
z wątku Pythona z szybkością 100 razy na sekundę w aplikacji, która działa na wielu platformach i była testowana przez tysiące godzin. Nigdy nie widziałem z tym żadnych problemów. Myślę, że jest to w porządku, o ile obiekt docelowy znajduje się w MainThread lub QThread. Umieściłem go również w ładnej bibliotece, zobacz qtutils .Wątki Pythona będą prostsze i bezpieczniejsze, a ponieważ są przeznaczone dla aplikacji opartych na I / O, są w stanie ominąć GIL. To powiedziawszy, czy rozważałeś nieblokujące wejścia / wyjścia za pomocą skręconych lub nieblokujących gniazd / wybierz?
EDYCJA: więcej na temat wątków
Wątki Pythona
Wątki Pythona są wątkami systemowymi. Jednak Python używa globalnej blokady interpretera (GIL), aby upewnić się, że interpreter zawsze wykonuje w danym momencie tylko blok instrukcji kodu bajtowego o określonej wielkości. Na szczęście Python zwalnia GIL podczas operacji wejścia / wyjścia, dzięki czemu wątki są przydatne do symulowania nieblokujących operacji we / wy.
Ważne ostrzeżenie: może to być mylące, ponieważ liczba instrukcji kodu bajtowego nie odpowiada liczbie wierszy w programie. Nawet pojedyncze przypisanie może nie być atomowe w Pythonie, więc blokada mutex jest konieczna dla każdego bloku kodu, który musi być wykonywany atomowo, nawet z GIL.
Wątki QT
Kiedy Python przekazuje kontrolę skompilowanemu modułowi strony trzeciej, zwalnia GIL. Obowiązkiem modułu jest zapewnienie atomowości tam, gdzie jest to wymagane. Kiedy kontrola jest przekazywana z powrotem, Python użyje GIL. Może to utrudniać korzystanie z bibliotek innych firm w połączeniu z wątkami. Korzystanie z zewnętrznej biblioteki wątków jest jeszcze trudniejsze, ponieważ zwiększa to niepewność, gdzie i kiedy kontrola jest w rękach modułu, a kiedy interpretera.
Wątki QT działają z wydanym GIL. Wątki QT mogą jednocześnie wykonywać kod biblioteki QT (i inny skompilowany kod modułu, który nie pobiera GIL). Jednak kod Pythona wykonywany w kontekście wątku QT nadal uzyskuje GIL i teraz musisz zarządzać dwoma zestawami logiki do blokowania kodu.
W końcu zarówno wątki QT, jak i wątki Pythona są opakowaniami wokół wątków systemowych. Wątki Pythona są marginalnie bezpieczniejsze w użyciu, ponieważ te części, które nie są napisane w Pythonie (niejawnie przy użyciu GIL), używają GIL w każdym przypadku (chociaż powyższe zastrzeżenie nadal ma zastosowanie).
Nieblokujące we / wy
Wątki niezwykle komplikują Twoją aplikację. Zwłaszcza, gdy mamy do czynienia z już złożoną interakcją między interpretera Pythona a skompilowanym kodem modułu. Podczas gdy wielu osobom trudno jest śledzić programowanie oparte na zdarzeniach, nieblokujące operacje we / wy oparte na zdarzeniach są często znacznie trudniejsze do rozważenia niż wątki.
W przypadku asynchronicznych operacji we / wy zawsze można mieć pewność, że dla każdego otwartego deskryptora ścieżka wykonania jest spójna i uporządkowana. Istnieją oczywiście kwestie, którymi należy się zająć, na przykład co zrobić, gdy kod zależny od jednego otwartego kanału dalej zależy od wyników wywołania kodu, gdy inny otwarty kanał zwraca dane.
Jednym z fajnych rozwiązań dla nieblokujących wejść / wyjść opartych na zdarzeniach jest nowa biblioteka Diesel . W tej chwili jest ograniczony do Linuksa, ale jest niezwykle szybki i dość elegancki.
Warto również poświęcić czas na naukę pyevent , opakowania otaczającego wspaniałą bibliotekę libevent, która zapewnia podstawową strukturę programowania opartego na zdarzeniach przy użyciu najszybszej dostępnej metody dla Twojego systemu (określanej w czasie kompilacji).
źródło
Zaletą
QThread
jest to, że jest zintegrowany z resztą biblioteki Qt. Oznacza to, że metody obsługujące wątki w Qt będą musiały wiedzieć, w którym wątku działają, i aby przenosić obiekty między wątkami, trzeba będzie ich użyćQThread
. Inną przydatną funkcją jest uruchamianie własnej pętli zdarzeń w wątku.Jeśli uzyskujesz dostęp do serwera HTTP, powinieneś rozważyć
QNetworkAccessManager
.źródło
QNetworkAccessManager
wygląda obiecująco. Dzięki.Zadałem sobie to samo pytanie, pracując dla PyTalk .
Jeśli używasz Qt, musisz użyć,
QThread
aby móc korzystać z frameworka Qt, a zwłaszcza systemu sygnału / gniazda.Dzięki silnikowi sygnału / slotów będziesz mógł rozmawiać z jednego wątku na drugi iz każdą częścią projektu.
Co więcej, nie ma zbytniej kwestii wydajności dotyczącej tego wyboru, ponieważ oba są powiązaniami C ++.
Oto moje doświadczenie z PyQt i wątkiem.
Zachęcam do korzystania
QThread
.źródło
Jeff ma kilka dobrych punktów. Tylko jeden główny wątek może wykonywać aktualizacje GUI. Jeśli musisz zaktualizować GUI z poziomu wątku, kolejkowane sygnały połączenia Qt-4 ułatwiają wysyłanie danych między wątkami i zostaną automatycznie wywołane, jeśli używasz QThread; Nie jestem pewien, czy będą, jeśli używasz wątków Pythona, chociaż łatwo jest dodać parametr do
connect()
.źródło
Nie mogę też polecić, ale mogę spróbować opisać różnice między wątkami CPython i Qt.
Po pierwsze, wątki CPythona nie działają współbieżnie, a przynajmniej nie kod Pythona. Tak, tworzą wątki systemowe dla każdego wątku Pythona, jednak tylko wątek aktualnie posiadający Global Interpreter Lock może działać (rozszerzenia C i kod FFI mogą go ominąć, ale kod bajtowy Pythona nie jest wykonywany, gdy wątek nie przechowuje GIL).
Z drugiej strony mamy wątki Qt, które są w zasadzie wspólną warstwą nad wątkami systemowymi, nie mają globalnej blokady interpretera, a zatem mogą działać współbieżnie. Nie jestem pewien, jak radzi sobie z tym PyQt, jednak jeśli twoje wątki Qt nie wywołują kodu Pythona, powinny być w stanie działać jednocześnie (z wyjątkiem różnych dodatkowych blokad, które mogą być zaimplementowane w różnych strukturach).
W celu dodatkowego dostrojenia można zmodyfikować liczbę instrukcji kodu bajtowego, które są interpretowane przed zmianą własności GIL - niższe wartości oznaczają więcej przełączania kontekstu (i prawdopodobnie wyższą responsywność), ale niższą wydajność na pojedynczy wątek (przełączniki kontekstu mają swój koszt - jeśli spróbuj zmieniać co kilka instrukcji, co nie przyspiesza).
Mam nadzieję, że pomoże to w twoich problemach :)
źródło
Nie mogę wypowiedzieć się na temat dokładnych różnic między Python i PyQt wątków, ale robiłem to, co próbujesz zrobić za pomocą
QThread
,QNetworkAcessManager
i upewniając się do rozmowyQApplication.processEvents()
, gdy wątek jest żywe. Jeśli reakcja GUI jest naprawdę problemem, który próbujesz rozwiązać, to później pomoże.źródło
QNetworkAcessManager
nie wymaga wątku aniprocessEvents
. Wykorzystuje asynchroniczne operacje we / wy.QNetworkAcessManager
ihttplib2
. Mój kod asynchroniczny używahttplib2
.