본문 바로가기

코딩/Python

[Python/파이썬] PyQt5 사용시 필요한 쓰레드 개념 이해 : 파이썬 GUI 응답없음 해결 방법 - 1

반응형

 

※ 이 글을 쓰는 사람은 SW 비전공자입니다.

※ 개인 공부를 위해 정리하는 글이며, 작성한 코드들은 효율성, 깔끔함(?) 등과는 거리가 멀 수 있습니다.

 

1편 : 2021.03.06 - [코딩/Python] - [Python/파이썬] PyQT5 및 QT Designer 소개, .ui 파일 .py로 변환 방법

2편 : 2021.03.28 - [코딩/Python] - [Python/파이썬] PyQt5를 통한 GUI 구성 및 사용법 이해하기

3편 : 2021.03.29 - [코딩/Python] - [Python/파이썬] PyQt5 사용시 필요한 쓰레드 개념 이해 - 1

4편 : 2021.03.30 - [코딩/Python] - [Python/파이썬] PyQt5 사용시 필요한 쓰레드 개념 이해-2 (GUI 응답없음 해결)

5편 : 2021.07.19 - [코딩/Python] - [Python/파이썬] PyQt5 - 시그널(Signal)과 슬롯(Slot) 개념

6편 : 2021.07.22 - [코딩/Python] - [Python/파이썬] PyQt5 - 사용자 정의 시그널(Custom Signal)과 Emit 사용법


  이전편에서 GUI를 짜고 버튼과 연동하여 Progress bar와 text 창에 진행상황을 띄우는 간단한 코드를 작성해보았다.

실제 복잡한 GUI 프로그램을 코딩하다 보니 PyQt가 주요 함수가 동작하는 와중에 멈춰버리는(응답없음) 현상을 확인했다. 이 때 쓰레드라는 개념을 활용해야한다고 한다.

 

  예를 들면 아래와 같은 경우다. 20km 경주를 진행하는 경주마1과 경주마 2가 있다. 각 버튼을 누르면 각 경주마는 출발하여 진행거리를 표시한다. '경주마 1출발'과 '경주마 2출발' 버튼을 각각 눌러서 2마리의 말이 달리는 진행상황을 보려고 한다.

 

< 경주마 프로그램 GUI 예시>

   

  위에 구성한 경주마 GUI 프로그램을 파이썬으로 아래처럼 코딩해보았다. 이전 포스팅에서 각 위젯에 접근할 때 ObjectName을 사용한다고 정리한 바가 있다.(위 프로그램의 경우 경주마 버튼 2개가 해당된다.) 

 

<코드>

import time
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
from PyQt5.QtCore import *

#UI파일 연결
#단, UI파일은 Python 코드 파일과 같은 디렉토리에 위치해야한다.
form_class = uic.loadUiType("test_pyqt2.ui")[0]

class WindowClass(QMainWindow, form_class) :
    def __init__(self) :
        super().__init__()
        self.setupUi(self)

        #각 버튼에 대한 함수 연결
        self.runButton1.clicked.connect(self.actionFunction1)
        self.runButton2.clicked.connect(self.actionFunction2)

    #경주마1 출발 버튼을 눌렀을 때 실행 될 메서드
    def actionFunction1(self):
        print("경주마1이 출발하였습니다.")
        for i in range(20):
            print("경주마1이"+str(i)+"km째 달리고 있습니다.")
            time.sleep(2)            
        print("경주마1이 결승지에 도착하였습니다.")
    
    #경주마2 출발 버튼을 눌렀을 때 실행 될 메서드
    def actionFunction2(self):
        print("경주마2가 출발하였습니다.")
        for i in range(20):
            time.sleep(2)
            print("경주마2가"+str(j)+"km째 달리고 있습니다.")
        print("경주마2가 결승지에 도착하였습니다.")

if __name__ == "__main__" :
    app = QApplication(sys.argv)
    myWindow = WindowClass()
    myWindow.show()
    app.exec_()

 

<결과>

  -. 먼저 '경주마 1 출발'을 눌러보면 아래와 같이 진행상황이 정상적으로 출력 된다.

 

<경주마 1 출발시>

  -. 위 경주마1이 달리고 있는 상황에서 '경주마 2 출발'을 눌러보자. 그럼 GUI 프로그램이 멈추고 동작하지 않는다. 경주마2를 출발시키려면 경주마 1이 20km 모두 달릴 때까지 기다려야 한다.

 

< 경주마 1이 달리는 동안 GUI는 응답없음 >

 

 

-. 그럼 만약 경주마 1이 달리는 동안 GUI도 동작하게 하고 경주마2도 출발시키려면 어떻게 해야할까???

 

    ▶ 이러한 부분을 가능하게 해주는 개념이 프로그래밍의 쓰레드 개념이라고 한다.

 

 

  

 

 * 쓰레드란?

  구글링을 하면 여러 참고할 수 있는 자료를 찾아볼 수 있지만 내가 이해한대로 정리해보았다.

 

  일단 쓰레드의 사전적 의미는 프로그램이 실행되는 '최소 단위'이다. 말 그대로 프로그램 1개를 실행할 때 이 것을 '프로세스'라고 하는데 이 프로세스 안에는 여러개의 쓰레드가 들어갈 수 있다. 파이썬은 인터프리터 언어라서  어떤 코드를 실행시킬 때, 1개의 스레드만 배분한다고 한다.  즉, 1개의 스레드에서 코드가 순차적으로 실행되기 때문에 위와 같이 Pyqt로 코딩 된 UI와 실제 동작하는 함수 코드가 같이 동작할 수 없게 되어 있다.(경주마 1 메서드가 끝날 때까지 경주마 2 메서드를 동작시킬 수 없음)

 

< Single Thread 개념도(1개 Thread) >

 

 

  그래서 파이썬의 내장 모듈인 Threading이나 PyQt5(GUI) 기반의 QThread 모듈을 사용해서 별도의 쓰레드를 구현해주어야 한다. 근데 찾아보니 파이썬에서 여러개의 쓰레드를 구현할 수 있으나 실질적으로는 GIL(Global Interpreter Lock)이라는 메커니즘 때문에 결국에는 1개의 쓰레드로 구현하는 것이나 마찬가지라고 한다. 

 

  간단히 말하자면 쓰레드는 여러개로 구현은 가능하나 실행은 번갈아가면서 1개 Thread만 진행된다는 것이다.(동시 실행 불가)

이부분은 잘 정리해주신 블로거가 있어서 참고링크를 글 하단에 걸어두었다.

 

< 파이썬 Multi Thread 개념도 - GIL(Global Interpreter Lock)

 

  

 

 

  다음 포스팅에서는 파이썬 PyQt 의 QThread 모듈을 활용하여 위 경주마 프로그램 코드에 적용해볼 예정이다.

 


※ 참고링크 : 파이썬 Thread 및 GIL 개념에 대해서 참고한 링크입니다.

1. '잉구블로그' 티스토리 : wangin9.tistory.com/entry/pythonthreadGIL

2. '수학과의 좌충 우돌 프로그래밍' 티스토리 : ssungkang.tistory.com/entry/python-GIL-Global-interpreter-Lock%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C

 

 

 

 

 

728x90