본문 바로가기
코딩취미/Python

파이썬에서 안전하게 멀티스레드 생성 및 관리하기(QThreadPool, QRunnable)

by 브링블링 2025. 2. 28.
반응형

PySide6에서 안전하게 멀티스레드 생성 및 관리하기

PySide6에서 UI가 멈추지 않도록 백그라운드에서 여러 개의 작업을 동시에 실행하려면 멀티스레딩(Multithreading) 을 활용해야 합니다. 하지만 멀티스레드를 제대로 관리하지 않으면 데이터 충돌, 성능 저하, 프로그램 충돌과 같은 문제가 발생할 수 있습니다. 이 글에서는 멀티스레드의 개념을 설명하고, PySide6에서 QThreadPoolQRunnable을 사용하여 안전하게 멀티스레드를 생성하고 관리하는 방법을 설명합니다. 


1. 멀티스레드란?

🔍 멀티스레드 개념 쉽게 이해하기

멀티스레딩(Multithreading)이란 하나의 프로그램 내에서 여러 개의 스레드를 실행하여 동시에 여러 작업을 수행하는 기술입니다.

🖥️ 멀티스레드의 실생활 예시

  1. 웹 브라우저: 여러 개의 탭을 동시에 로드하고 사용자가 입력을 할 수 있음.
  2. 비디오 스트리밍 앱: 영상 재생 중에도 네트워크에서 데이터를 지속적으로 다운로드함.
  3. 게임: 그래픽 렌더링, AI 연산, 사용자 입력 처리 등이 동시에 수행됨.
  4. 파일 다운로드 관리자: 여러 개의 파일을 동시에 다운로드함.
  5. 데이터 처리 프로그램: 여러 개의 CPU 코어를 활용하여 연산 속도를 높임.

⚠️ 멀티스레드 사용 시 주의할 점

  • 여러 개의 스레드가 공유 자원(예: 리스트, 딕셔너리)을 수정할 경우 데이터 충돌이 발생할 수 있음.
  • 너무 많은 스레드를 생성하면 CPU 사용량이 증가하여 오히려 성능이 저하될 수 있음.
  • UI 업데이트는 반드시 메인 스레드에서 실행해야 함.

2. QThreadPool과 QRunnable이란?

🏗 QThreadPool이란?

QThreadPool은 여러 개의 작업을 효율적으로 실행할 수 있도록 스레드 풀(Thread Pool) 을 관리하는 클래스입니다.

  • 일반적으로 프로그램에서 실행할 스레드의 개수를 너무 많이 만들면 성능이 저하될 수 있습니다.
  • QThreadPool을 사용하면 운영 체제에서 적절한 개수의 스레드를 관리하여 성능을 최적화합니다.
  • globalInstance()를 사용하면 전역적으로 사용할 수 있는 QThreadPool을 가져올 수 있습니다.

🎯 QRunnable이란?

QRunnableQThreadPool에서 실행할 작업 단위(Task) 를 정의하는 클래스입니다.

  • QRunnable을 상속받아 run() 메서드를 구현하면 됩니다.
  • QThread처럼 개별적으로 스레드를 생성하는 것이 아니라, QThreadPool을 통해 관리됩니다.
  • start(worker)를 호출하면 스레드 풀에서 자동으로 작업을 실행합니다.

3. PySide6에서 멀티스레드를 안전하게 생성하고 관리하는 방법

PySide6에서는 QThreadQThreadPool을 사용하여 멀티스레드를 구현할 수 있습니다. 일반적으로 여러 개의 단순 작업을 병렬로 실행할 경우 QThreadPool + QRunnable을 사용하는 것이 더 적합합니다.

📌 필요한 모듈

  • PySide6.QtCore 모듈: QThread, QThreadPool, QRunnable, Signal
  • PySide6.QtWidgets 모듈: UI 구성에 필요한 QApplication, QPushButton, QVBoxLayout, QWidget

4. 예제 코드

다음 예제에서는 QThreadPoolQRunnable을 활용하여 멀티스레드를 실행하는 방법을 보여줍니다.

반응형

✏️ 코드 설명

  1. Worker 클래스는 QRunnable을 상속받아 실행됩니다.
  2. run() 메서드에서 각 스레드가 수행할 작업을 정의합니다.
  3. QThreadPool을 활용하여 여러 개의 스레드를 관리합니다.
  4. UI에서 버튼을 눌러 여러 개의 스레드를 실행할 수 있습니다.
import sys
import time
from PySide6.QtCore import QRunnable, QThreadPool, QThread, Signal
from PySide6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget

class Worker(QRunnable):
    def __init__(self, task_id):
        super().__init__()
        self.task_id = task_id

    def run(self):
        for i in range(5):
            print(f"작업 {self.task_id} 실행 중: {i}")
            time.sleep(1)  # 작업 실행 대기
        print(f"작업 {self.task_id} 완료!")

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
    
    def initUI(self):
        self.setWindowTitle("QThreadPool 멀티스레드 예제")
        self.setGeometry(100, 100, 300, 200)
        
        self.button_start = QPushButton("멀티스레드 실행", self)
        self.button_start.clicked.connect(self.start_threads)
        
        layout = QVBoxLayout()
        layout.addWidget(self.button_start)
        self.setLayout(layout)
        
        self.thread_pool = QThreadPool.globalInstance()
    
    def start_threads(self):
        for i in range(3):  # 3개의 작업 실행
            worker = Worker(i)
            self.thread_pool.start(worker)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

5. 테이블 정리

항목 설명
QThreadPool 여러 개의 스레드를 효율적으로 관리하는 클래스
QRunnable 스레드에서 실행할 작업을 정의하는 클래스
run() 각 스레드에서 실행할 작업을 정의하는 메서드
start(worker) 작업을 스레드 풀에서 실행하는 메서드
globalInstance() 전역적으로 사용할 수 있는 QThreadPool 인스턴스 반환

반응형