PyQt 애플리케이션에서 스레딩 : Qt 스레드 또는 Python 스레드를 사용합니까?


116

웹 연결을 통해 정기적으로 데이터를 검색하는 GUI 응용 프로그램을 작성하고 있습니다. 이 검색에는 시간이 걸리므로 검색 프로세스 중에 UI가 응답하지 않게됩니다 (작은 부분으로 분할 할 수 없음). 이것이 제가 웹 연결을 별도의 작업자 스레드에 아웃소싱하려는 이유입니다.

[예, 알아요, 이제 두 가지 문제가 있습니다.]

어쨌든 응용 프로그램은 PyQt4를 사용하므로 Qt의 스레드를 사용하거나 Python threading모듈을 사용하는 것이 더 나은 선택인지 알고 싶습니다 . 각각의 장단점은 무엇입니까? 아니면 완전히 다른 제안이 있습니까?

편집 (바운티) : 제 특별한 경우의 솔루션은 아마도 Jeff OberLukáš Lalinský 와 같은 비 차단 네트워크 요청을 사용하는 것이지만 (따라서 기본적으로 동시성 문제는 네트워킹 구현에 남겨 둡니다), 저는 여전히 더 많은 것을 원합니다. 일반적인 질문에 대한 심도있는 답변 :

( threading모듈에서) 네이티브 Python 스레드보다 PyQt4 (즉 Qt) 스레드를 사용하는 장점과 단점은 무엇입니까 ?


편집 2 : 답변 해 주셔서 감사합니다. 100 % 동의는 없지만, 그 장점은 라이브러리의 나머지 부분과의 통합이면서도 실질적인 불이익을 유발하지 않기 때문에 대답이 "Qt 사용"이라는 광범위한 합의가있는 것 같습니다.

두 개의 스레딩 구현 중 하나를 선택하려는 사람에게는 abbot이 연결 하는 PyQt 메일 링 목록 스레드를 포함하여 여기에 제공된 모든 답변을 읽어 보는 것이 좋습니다 .

현상금에 대해 몇 가지 답변을 고려했습니다. 결국 나는 매우 관련성이 높은 외부 참조를 위해 abbot을 선택했습니다. 그러나 그것은 긴밀한 요청이었습니다.

다시 한 번 감사드립니다.

답변:


106

이것은 얼마 전에 PyQt 메일 링리스트에서 논의 되었습니다 . 주제에 대한 Giovanni Bajo의 의견 을 인용 합니다.

거의 동일합니다. 주요 차이점은 QThreads가 Qt (비동기 신호 / 슬롯, 이벤트 루프 등)와 더 잘 통합된다는 것입니다. 또한 Python 스레드에서 Qt를 사용할 수 없습니다 (예를 들어 QApplication.postEvent를 통해 메인 스레드에 이벤트를 게시 할 수 없음). 작동하려면 QThread가 필요합니다.

경험상 일반적인 규칙은 Qt와 어떤 식 으로든 상호 작용하려는 경우 QThreads를 사용하고 그렇지 않으면 Python 스레드를 사용하는 것입니다.

그리고 PyQt의 저자는이 주제에 대해 이전에 언급했습니다 : "둘 다 동일한 네이티브 스레드 구현을 둘러싼 래퍼입니다". 그리고 두 구현 모두 동일한 방식으로 GIL을 사용합니다.


2
좋은 대답이지만, 실제로 요약하지 않고 메일 링리스트에서 Giovanni Bajo를 인용하고있는 부분을 명확하게 보여주기 위해 blockquote 버튼을 사용해야한다고 생각합니다. :)
c089

2
왜 QApplication.postEvent ()를 통해 메인 스레드에 이벤트를 게시 할 수없고 QThread가 필요한지 궁금합니다. 나는 사람들이 그것을하는 것을 보았고 그것이 효과가 있다고 생각합니다.
Trilarion

1
QCoreApplication.postEvent크로스 플랫폼을 실행하고 1000 시간 동안 테스트를 거친 애플리케이션에서 초당 100 번의 속도로 Python 스레드에서 호출 했습니다. 나는 그로부터 어떤 문제도 본 적이 없습니다. 대상 개체가 MainThread 또는 QThread에있는 한 괜찮다고 생각합니다. 또한 멋진 라이브러리에 정리했습니다 . qtutils를 참조하십시오 .
three_pineapples

2
이 질문과 답변의 높은 투표 특성을 감안할 때 Python 스레드의 특정 Qt 메서드를 사용하는 것이 안전한 조건에 대해 자세히 설명 하는 ekhumoro최근 SO 답변을 지적 할 가치가 있다고 생각합니다 . 이것은 나 자신과 @Trilarion이 본 관찰 된 행동으로 집계됩니다.
three_pineapples

33

Python의 스레드는 더 간단하고 안전하며 I / O 기반 애플리케이션을위한 것이므로 GIL을 우회 할 수 있습니다. 즉, Twisted 또는 non-blocking 소켓 / select를 사용하는 non-blocking I / O를 고려 했습니까?

편집 : 스레드에 대한 추가 정보

파이썬 스레드

Python의 스레드는 시스템 스레드입니다. 그러나 Python은 글로벌 인터프리터 잠금 (GIL)을 사용하여 인터프리터가 한 번에 특정 크기의 바이트 코드 명령 블록 만 실행하도록합니다. 다행히도 Python은 입력 / 출력 작업 중에 GIL을 릴리스하므로 스레드가 비 블로킹 I / O를 시뮬레이션하는 데 유용합니다.

중요한주의 사항 : 바이트 코드 명령어 의 수가 프로그램의 행 수와 일치 하지 않기 때문에 이것은 오해의 소지가 있습니다 . 뮤텍스 잠금이 필요합니다, 그래서 심지어 하나의 과제는, 파이썬에서 원자하지 않을 수 있는 , 심지어 GIL로, 원자 적으로 실행해야하는 코드의 블록.

QT 스레드

Python이 타사 컴파일 모듈에 제어권을 넘겨 주면 GIL이 해제됩니다. 필요한 경우 원 자성을 보장하는 것은 모듈의 책임이됩니다. 제어가 다시 전달되면 Python은 GIL을 사용합니다. 이로 인해 스레드와 함께 타사 라이브러리를 사용하면 혼란 스러울 수 있습니다. 외부 스레딩 라이브러리를 사용하는 것은 모듈 대 인터프리터의 손에 제어가 언제 어디서 있는지에 대한 불확실성을 추가하기 때문에 훨씬 더 어렵습니다.

QT 스레드는 GIL이 릴리스 된 상태에서 작동합니다. QT 스레드는 QT 라이브러리 코드 (및 GIL을 획득하지 않는 기타 컴파일 된 모듈 코드)를 동시에 실행할 수 있습니다. 그러나 QT 스레드의 컨텍스트 내에서 실행되는 Python 코드 는 여전히 GIL을 획득하므로 이제 코드를 잠그기 위해 세트의 논리 를 관리 해야합니다.

결국 QT 스레드와 Python 스레드는 모두 시스템 스레드를 둘러싼 래퍼입니다. Python 스레드는 사용하기에 약간 더 안전합니다. Python으로 작성되지 않은 부분 (암시 적으로 GIL 사용)은 어떤 경우에도 GIL을 사용하기 때문입니다 (위의주의 사항은 여전히 ​​적용됨).

비 차단 I / O

스레드는 응용 프로그램을 매우 복잡하게 만듭니다. 특히 파이썬 인터프리터와 컴파일 된 모듈 코드 사이의 이미 복잡한 상호 작용을 다룰 때. 많은 사람들이 이벤트 기반 프로그래밍을 따르기 어렵다고 생각하지만, 이벤트 기반 비 차단 I / O는 스레드보다 추론하기가 훨씬 덜 어렵습니다.

비동기 I / O를 사용하면 열려있는 각 설명자에 대해 실행 경로가 일관되고 질서 정연하다는 것을 항상 확인할 수 있습니다. 하나의 개방형 채널에 의존하는 코드가 다른 개방형 채널이 데이터를 반환 할 때 호출되는 코드의 결과에 따라 달라지는 경우와 같이 해결해야 할 문제가 있습니다.

이벤트 기반 비 차단 I / O를위한 좋은 솔루션 중 하나는 새로운 Diesel 라이브러리입니다. 현재 Linux로 제한되어 있지만 매우 빠르고 매우 우아합니다.

시스템에 가장 빠른 방법을 사용하여 이벤트 기반 프로그래밍을위한 기본 프레임 워크를 제공하는 멋진 libevent 라이브러리를 둘러싼 래퍼 인 pyevent 를 배우는 것도 시간 가치가 있습니다 (컴파일 시간에 결정됨).


Re Twisted etc .: 실제 네트워킹 작업을 수행하는 타사 라이브러리를 사용합니다. 나는 그 안에 패치를 피하고 싶습니다. 하지만 아직 조사하겠습니다. 감사합니다.
balpha

2
실제로 GIL을 우회하는 것은 없습니다. 그러나 Python은 I / O 작업 중에 GIL을 릴리스합니다. 파이썬은 또한 GIL 자체를 획득 / 해제하는 책임이있는 컴파일 된 모듈에 '인계'할 때 GIL을 해제합니다.
Jeff Ober

2
업데이트가 잘못되었습니다. Python 코드는 QThread와 Python 스레드에서 정확히 동일한 방식으로 실행됩니다. Python 코드를 실행할 때 GIL을 무죄로 인정하고 (그런 다음 Python이 스레드 간 실행을 관리함) C ++ 코드를 실행할 때 GIL을 해제합니다. 전혀 차이가 없습니다.
Lukáš Lalinský

1
아니요, 요점은 스레드를 생성하는 방법에 관계없이 Python 인터프리터가 신경 쓰지 않는다는 것입니다. 관심있는 것은 GIL을 획득 할 수 있고 X 명령 후에이를 해제 / 재 획득 할 수 있다는 것입니다. 예를 들어 ctypes를 사용하여 C 라이브러리에서 콜백을 생성 할 수 있습니다.이 콜백은 별도의 스레드에서 호출되며 코드는 다른 스레드임을 알지 못해도 잘 작동합니다. 스레드 모듈에는 특별한 것이 없습니다.
Lukáš Lalinský

1
QThread가 잠금과 관련하여 어떻게 다른지 그리고 "코드를 잠그기 위해 두 세트의 논리를 관리해야합니다"라고 말씀하셨습니다. 내가 말하는 것은 전혀 다르지 않다는 것입니다. ctypes 및 pthread_create를 사용하여 스레드를 시작할 수 있으며 정확히 동일한 방식으로 작동합니다. Python 코드는 GIL에 대해 신경 쓸 필요가 없습니다.
Lukáš Lalinský

21

의 장점은 QThread나머지 Qt 라이브러리와 통합된다는 것입니다. 즉, Qt의 스레드 인식 메서드는 실행되는 스레드를 알아야하며 스레드간에 개체를 이동하려면 QThread. 또 다른 유용한 기능은 스레드에서 자체 이벤트 루프를 실행하는 것입니다.

HTTP 서버에 액세스하는 경우 QNetworkAccessManager.


1
Jeff Ober의 답변에 대해 내가 언급 한 것 외에도 QNetworkAccessManager유망 해 보입니다. 감사.
balpha

13

PyTalk 작업을 할 때도 같은 질문을 했습니다 .

Qt QThread를 사용하는 경우 Qt 프레임 워크와 특히 신호 / 슬롯 시스템을 사용하려면을 사용해야합니다.

신호 / 슬롯 엔진을 사용하면 스레드에서 다른 스레드로 그리고 프로젝트의 모든 부분과 대화 할 수 있습니다.

또한 둘 다 C ++ 바인딩이기 때문에이 선택에 대한 성능 문제는 그리 많지 않습니다.

다음은 PyQt와 스레드에 대한 나의 경험입니다.

을 사용하는 것이 좋습니다 QThread.


9

Jeff는 몇 가지 좋은 점을 가지고 있습니다. 하나의 주 스레드 만 GUI 업데이트를 수행 할 수 있습니다. 스레드 내에서 GUI를 업데이트해야하는 경우 Qt-4의 대기 연결 신호를 사용하면 스레드간에 데이터를 쉽게 전송할 수 있으며 QThread를 사용하는 경우 자동으로 호출됩니다. Python 스레드를 사용하는지 여부는 확실하지 않지만 .NET Framework에 매개 변수를 추가하는 것은 쉽습니다 connect().


5

나도 정말 추천 할 수는 없지만 CPython과 Qt 스레드의 차이점을 설명해 볼 수 있습니다.

우선, CPython 스레드는 적어도 Python 코드가 아닌 동시에 실행되지 않습니다. 예, 각 Python 스레드에 대해 시스템 스레드를 생성하지만 현재 Global Interpreter Lock을 보유하고있는 스레드 만 실행할 수 있습니다 (C 확장 및 FFI 코드가이를 우회 할 수 있지만 스레드가 GIL을 보유하지 않는 동안 Python 바이트 코드는 실행되지 않음).

반면에 기본적으로 시스템 스레드에 대한 공통 계층 인 Qt 스레드는 Global Interpreter Lock이 없으므로 동시에 실행될 수 있습니다. PyQt가 어떻게 처리하는지 잘 모르겠지만 Qt 스레드가 Python 코드를 호출하지 않는 한 동시에 실행할 수 있어야합니다 (다양한 구조에서 구현 될 수있는 다양한 추가 잠금이 금지됨).

추가 미세 조정을 위해 GIL의 소유권을 전환하기 전에 해석되는 바이트 코드 명령의 양을 수정할 수 있습니다. 값이 낮을수록 컨텍스트 전환이 더 많고 응답 성이 높지만 개별 스레드 당 성능이 낮아집니다 (컨텍스트 전환에 비용이 있습니다. 속도에 도움이되지 않는 몇 가지 지침을 모두 전환 해보십시오.)

문제에 도움이되기를 바랍니다. :)


7
여기서 주목하는 것이 중요합니다. PyQt QThreads 는 Global Interpreter Lock을 사용 합니다. 모든 Python 코드는 GIL을 잠그고 PyQt에서 실행하는 모든 QThread는 Python 코드를 실행합니다. (그렇지 않으면 실제로 PyQt의 "Py"부분을 사용하고 있지 않습니다.). Python 코드에서 외부 C 라이브러리로 연기하도록 선택하면 GIL이 릴리스되지만 Python 스레드를 사용하든 Qt 스레드를 사용하든 관계없이 마찬가지입니다.
쿼크

그것이 실제로 제가 전달하려고했던 것입니다. 모든 Python 코드가 잠금을 취하지 만 별도의 스레드에서 실행되는 C / C ++ 코드에는 문제가되지 않습니다
p_l

0

파이썬과 PyQt는 스레드 사이의 정확한 차이에 대해 언급 할 수 없습니다,하지만 난 당신이 사용 할 시도하고 일을 봤는데 QThread, QNetworkAcessManager및 전화로 확인하는 QApplication.processEvents()스레드가 살아있다. GUI 응답 성이 실제로 해결하려는 문제라면 나중에 도움이 될 것입니다.


1
QNetworkAcessManager스레드 또는 processEvents. 비동기 IO 작업을 사용합니다.
Lukáš Lalinský

죄송합니다 ... 예, QNetworkAcessManager및 의 조합을 사용 하고 httplib2있습니다. 내 비동기 코드는 httplib2.
brianz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.