- 스레드란?
운영체제에는 Window, macOS, Linux 와 같은 것이 있는데 컴퓨터를 전체적으로 관리하는 매니저 역할을 합니다.
프로그램은 이 운영체제 위에 메모리에 올라가서 실행하는 데 이것을 프로세스(process)라고 합니다.
프로세스의 실행 단위를 스레드라고 하는 데 프로세스는 최소 하나 이상의 스레드를 가지고 동작합니다.
우리가 윈도우에서 메신저도 사용하고 게임도 하고 문서작성도하고 인터넷도 사용할 경우 실행되는 여러 프로그램을
잘 관리해야하는데 이런 작업을 스케줄링이라고 합니다. 운영체제는 스케쥴의 단위로 스레드를 사용합니다.
프로그램을 작성할 때 여러 개의 스레드를 사용할 수 있도록 하는 데 이를 멀티스레드라고 합니다.
이처럼 프로그램을 작성할 때 멀티스레드 형태로 구현하면 운영체제에서 동시에 스케쥴링될 수 있게 때문에
보통 성능이 더 좋아집니다.
60-1 예제: ex59
PyQt의 QThread 클래스를 사용해서 스레드를 생성합니다.
QThread 클래스를 상속받는 Worker 클래스를 정의합니다.
Worker 클래스는 run() 메서드를 구현하는데 기본적으로 while 문을 사용한 무한 루프 구조를 갖습니다.
무한 루프 내에서 원하는 작업을 기술합니다.
import sys from PyQt5.QtWidgets import QApplication, QWidget, QPlainTextEdit, QScrollBar from PyQt5.QtCore import Qt, QThread class Worker(QThread): def __init__(self, widget): super().__init__() font_kind = QFont("맑은 고딕", 10) self.plainTextEdit = QPlainTextEdit(widget) self.plainTextEdit.setFont(font_kind) self.plainTextEdit.addScrollBarWidget(QScrollBar(Qt.Vertical), Qt.AlignRight) self.plainTextEdit.move(10, 10) self.plainTextEdit.resize(280, 180) def run(self): while True: self.plainTextEdit.appendPlainText("안녕하세요") self.sleep(1) class MyApp(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.worker = Worker(self) self.worker.start() self.setGeometry(300, 300, 300, 200) self.setWindowTitle("Thread Test") self.show() if __name__ == "__main__": app = QApplication(sys.argv) win = MyApp() sys.exit(app.exec_()) |
60-2 설명
PlainTextEdit 에 "안녕하세요"를 출력하도록 했습니다. 무한 루프이기 때문에 해당 작업은 계속 실행됩니다.
따라서 QThread 클래스에 정의된 sleep 메서드를 통해 적당한 시간 동안 스레드가 실행되지 않도록 sleep 을 해줍니다.
self.worker = Worker(self) self.worker.start() |
Worker 클래스 생성자를 통하여 worker 객체를 만들고 start() 메서드를 호출합니다.
start() 메서드는 Worker 클래스에 없지만 QThread 클래스에 정의되어 있기 때문에 호출할 수 있습니다.
start() 메서드를 호출하면 자동적으로 run() 메서드를 호출해줍니다. 따라서 Worker 클래스의 run() 메서드가 실행되면서
1초에 한 번씩 "안녕하세요"가 출력됩니다.
60-3 결과
60-4 예제2 : 값의 전달
스레드를 사용하면 여러 객체가 동시에 스케쥴링 됩니다. 이때 한 객체에서 다른 객체로 값을 전달하는 경우 시그널을 사용할 수 있습니다.
여기에서 Worker 클래스의 객체에서 MyApp 클래스 객체로 값을 전달해 보겠습니다.
import sys from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot class Worker(QThread): timeout = pyqtSignal(int) # 사용자 시그널 정의 def __init__(self): super().__init__() self.num = 0 # 초기값 설정 def run(self): while True: self.timeout.emit(self.num) # 방출 self.num += 1 self.sleep(1) class MyApp(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.worker = Worker() self.worker.start() self.worker.timeout.connect(self.timeout) self.edit = QLineEdit(self) self.edit.move(10, 10) self.setGeometry(300, 300, 300, 200) self.setWindowTitle("Thread deliver data to object") self.show() @pyqtSlot(int) def timeout(self, arg1): self.edit.setText(str(arg1)) if __name__ == "__main__": app = QApplication(sys.argv) win = MyApp() sys.exit(app.exec_()) |
60-5 설명
class Worker(QThread): timeout = pyqtSignal(int) # 사용자 시그널 정의 def __init__(self): super().__init__() self.num = 0 # 초기값 설정 def run(self): while True: self.timeout.emit(self.num) # 방출 self.num += 1 self.sleep(1) |
Worker 클래스에 사용자 정의 시그널인 timeout 객체를 생성합니다.
이 시그널은 객체를 통해 1초에 한 번 emit() 메서드를 호출합니다.
전달한 데이터는 Worker 클래스의 객체가 생성될 때 0으로 초기화하기 위해 생성자에 구현했습니다.
@pyqtSlot(int) def timeout(self, arg1): self.edit.setText(str(arg1)) |
MyApp 클래스에서는 시그널을 처리할 슬롯 메서드를 구현해 줍니다. 데이터가 전달되면 이를 QLineEdit 객체에 출력합니다.
60-6 결과
60-7 예제3: 스레드 컨트롤
실행된 스레드를 멈추고 다시 재실행해 보겠습니다.
QThread 클래스에는 스레드를 멈추는 pause(), 재실행하는 resume() 기능이 존재하지 않습니다.
따라서, 사용자가 스레드가 while 루프 조건의 boolean 변수를 사용해서 run() 메서드를 제어해야 합니다.
import sys from PyQt5.QtWidgets import QApplication, QWidget, QPlainTextEdit, QScrollBar, QPushButton from PyQt5.QtCore import Qt, QThread class Worker(QThread): def __init__(self, widget): super().__init__() self.plainTextEdit = QPlainTextEdit(widget) self.plainTextEdit.addScrollBarWidget(QScrollBar(Qt.Vertical), Qt.AlignRight) self.plainTextEdit.move(10, 10) self.plainTextEdit.resize(280, 150) self.plainTextEdit.show() self.running = True def run(self): while self.running: self.plainTextEdit.appendPlainText("Welcome to python!!") self.sleep(1) def resume(self): self.running = True def pause(self): self.running = False class MyApp(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.worker = Worker(self) self.worker.start() btn1 = QPushButton("resume", self) btn1.move(80, 160) btn1.clicked.connect(self.resume) btn2 = QPushButton("pause", self) btn2.move(160, 160) btn2.clicked.connect(self.pause) self.setGeometry(300, 300, 300, 200) self.setWindowTitle("Thread Resume & Pause") self.show() def resume(self): self.worker.resume() self.worker.start() def pause(self): self.worker.pause() if __name__ == "__main__": app = QApplication(sys.argv) win = MyApp() sys.exit(app.exec_()) |
60-8 결과
'프로그래밍 > PyQt5 GUI' 카테고리의 다른 글
62. 주가 챠트 (0) | 2021.08.12 |
---|---|
61. 주식 데이터 (0) | 2021.08.12 |
59. 이벤트 루프 (0) | 2021.08.10 |
58. 타이머와 스레드 (0) | 2021.08.09 |
57. 타이머 (0) | 2021.08.09 |