Python如何安全地挂起、恢复、终止Qthread线程
我是一个Python初学者,最近在用PyQt5做Gui的时候遇到一个烧脑的问题,如何安全的实现线程的暂停、终止。如果粗暴的kill掉线程,比如说线程正在requests一个数据,这样总感觉不太好,于是查阅了1晚上的各种资料(没有老师教,没地请教,只能自己动手~呜呜呜呜)。
为啥threading仅有start而没有end?
线程一般用在网络连接、释放系统资源、dump流文件,这些都跟IO相关了,你突然关闭线程那这些
没有合理地关闭怎么办?是不是就是给自己造bug呢?
经过各种资料的查询,有使用TerminateThread强行终止线程的,有使用thread.join阻塞线程的(原来我还不是特别明白),经过思考我想到了一直不借助任何其他库,用基本函数就可以实现的线程挂起、终止的办法。
简单来说,就是定义个线程stop状态的全局变量,每次线程循环的时候就检查一下这个变量是不是提示自己该停止或者暂停或者恢复了,而且不强行终止线程,还可以重新开启。废话不多说,直接上代码:
# An highlighted block
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QProgressBar, QPushButton
import time
class Worker(QThread):
valueChanged = pyqtSignal(int) # 值变化信号
status = pyqtSignal(int)
def run(self):
print('thread id', int(QThread.currentThreadId()))
# 循环发送信号
break2 = False #break标志量
for i in range(1, 101):
if stop_status == 1: #停止信号1
self.valueChanged.emit(0)
#这个地方如果有进程锁,需要在这里解锁,当然也可以直接用break,但是break会执行后面的语句
print('程序结束')
return
elif stop_status == 2: #暂停信号2
while 1:
if stop_status == 2:
time.sleep(0.1) #必须有time.sleep 可以有效降低cpu消耗
continue
elif stop_status == 1: #停止信号1
break2 = True #break标志量,外层继续执行break
break #跳出无限循环
else: #恢复信号0
break
else:
pass
if(break2): #跳出外层for循环
break
print('value', i)
self.valueChanged.emit(i)
time.sleep(0.5)
self.status.emit(0)
class Window(QWidget):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
# 垂直布局
layout = QVBoxLayout(self)
self.progressBar = QProgressBar(self)
self.progressBar.setRange(0, 100)
layout.addWidget(self.progressBar)
self.startButton = QPushButton('开启线程', self, clicked=self.onStart)
layout.addWidget(self.startButton)
self.suspendButton = QPushButton('挂起线程', self, clicked=self.onSuspendThread, enabled=False)
layout.addWidget(self.suspendButton)
self.resumeButton = QPushButton('恢复线程', self, clicked=self.onResumeThread, enabled=False)
layout.addWidget(self.resumeButton)
self.stopButton = QPushButton('终止线程', self, clicked=self.onStopThread, enabled=False)
layout.addWidget(self.stopButton)
# 当前线程id
print('main id', int(QThread.currentThreadId()))
# 子线程
self._thread = Worker(self)
#self._thread.finished.connect(self._thread.deleteLater)
self._thread.valueChanged.connect(self.progressBar.setValue)
def onStart(self):
global stop_status
stop_status = 0
print('main id', int(QThread.currentThreadId()))
self._thread.start() # 启动线程
self.startButton.setEnabled(False)
self.suspendButton.setEnabled(True)
self.stopButton.setEnabled(True)
def onSuspendThread(self):
global stop_status
stop_status = 2
print('挂起线程')
self.suspendButton.setEnabled(False)
self.resumeButton.setEnabled(True)
def onResumeThread(self):
global stop_status
stop_status = 0
print('恢复线程')
self.suspendButton.setEnabled(True)
self.resumeButton.setEnabled(False)
def onStopThread(self):
global stop_status
stop_status = 1
self.startButton.setEnabled(True)
self.resumeButton.setEnabled(False)
self.progressBar.setRange(0, 100)
print('终止线程')
self.stopButton.setEnabled(False)
if __name__ == '__main__':
import sys
import os
print('pid', os.getpid())
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())