python - 使用事件系统时如何正确杀死/退出/停止线程?
问题描述
我正在通过python创建一个gui工具。这里有一个问题,当我通过点击一个按钮触发一个子线程运行一些逻辑代码时。处理时间可能很长,并且在这个过程中可能会发生一些错误。如果发生错误我会发送一个错误事件来通知EventManager 并调用一些函数来杀死/退出子线程,因为继续运行左侧逻辑代码是没有意义的。但我不知道如何使用错误事件正确杀死/退出/停止子线程。有人可以帮我吗?
from queue import Queue, Empty
from threading import *
from tkinter import *
import time
from tkinter import ttk
EVENT_TYPE_1 = "Count"
EVENT_TYPE_2 = "Error"
MAX_NUMBER = 10
CUR_NUMBER = 0
class event_manager:
def __init__(self):
self._eventQueue = Queue()
self._thread = Thread(target=self.Run, daemon=True)
self._handlers = {}
self._active = False
def Start(self):
self._active = True
self._thread.start()
def Run(self):
while self._active is True:
try:
event = self._eventQueue.get(block=True, timeout=1)
self.Process(event)
except Empty:
pass
def Process(self, event):
if event.type in self._handlers:
for handler in self._handlers[event.type]:
handler()
else:
pass
def Stop(self):
self._active = False
self._thread.join()
def addEventListenter(self, type_, handler):
try:
handlerList = self._handlers[type_]
except KeyError:
handlerList = []
self._handlers[type_] = handlerList
if handler not in handlerList:
handlerList.append(handler)
def removeEventListenter(self, type_, handler):
try:
handlerList = self._handlers[type_]
if handler in handlerList:
handlerList.remove(handler)
if not handlerList:
del self._handlers[type_]
except KeyError:
pass
def sendEvent(self, event):
self._eventQueue.put(event)
class Event:
def __init__(self, event_event_name, cur_done_task, type_=None):
self.type = type_
self._event_name = event_event_name
self._curDoneTask = cur_done_task
class EventSource:
def __init__(self, event_name, event_mgr, max_number, type):
self._event_name = event_name
self._event_manager = event_mgr
self._type = type
self._max_number = max_number
def count(self):
global CUR_NUMBER
for i in range(self._max_number):
CUR_NUMBER = i + 1
if CUR_NUMBER == 4: # assume this is a error check function,if error occurs,it will send a error event,and hopefully this event can terminate the sub-thread which is exactly running current code
print("************ detect error occurred , this thread should be terminated immediately !")
errorEvent = Event("error", CUR_NUMBER, type_=EVENT_TYPE_2)
self._event_manager.sendEvent(errorEvent)
print(
"************ main thread start:now start process {} - count : {}".format(self._event_name, CUR_NUMBER))
event = Event("test", CUR_NUMBER, type_=self._type)
self._event_manager.sendEvent(event)
time.sleep(1)
class GUIListener(Tk):
def __init__(self):
super(GUIListener, self).__init__()
self.title("Progress GUI")
self.geometry("1200x805+600+100")
self.config(bg="#535353")
self.resizable(True, True)
self.taskThread = None
self.progressBar = ttk.Progressbar(master=self, orient=HORIZONTAL, maximum=MAX_NUMBER, length=300)
self.progressBar.pack()
self.button = ttk.Button(self, text="Run", command=lambda: self.button_function(MAX_NUMBER))
self.button.pack()
def update_progress_value(self):
print("************Sub thread start: detect progress bar value is now...{}".format(self.progressBar['value']))
self.progressBar['value'] = CUR_NUMBER
self.progressBar.update_idletasks()
print("************Sub thread start: update progress bar value to...{}".format(CUR_NUMBER))
def button_function(self, max_number):
# though I can make some error check function before task thread starts,but I really want to discuss the way that using event-system here
es = EventSource("eventSource", eventMgr, max_number, EVENT_TYPE_1)
self.taskThread = Thread(target=es.count, daemon=True).start() # here to start the sub-thread which need to be terminated by error event
def terminate_error_thread(self): # this method will be called when GUIListener recieved error event and terminate the sub-thread immediately
pass
# TODO: but how to implement this method?
if __name__ == '__main__':
gui = GUIListener()
eventMgr = event_manager()
eventMgr.addEventListenter(EVENT_TYPE_1, gui.update_progress_value)
eventMgr.addEventListenter(EVENT_TYPE_2, gui.terminate_error_thread)
eventMgr.Start()
gui.mainloop()
希望控制台会给我以下日志:
************ main thread start:now start process eventSource - count : 1
************Sub thread start: detect progress bar value is now...0.0
************Sub thread start: update progress bar value to...1
************ main thread start:now start process eventSource - count : 2
************Sub thread start: detect progress bar value is now...1
************Sub thread start: update progress bar value to...2
************ main thread start:now start process eventSource - count : 3
************Sub thread start: detect progress bar value is now...2
************Sub thread start: update progress bar value to...3
************ detect error occurred , this thread should be terminated immediately !
解决方案
In the count
function, you could simply break out of the loop when you want to exit. If the function finishes running, the thread will exit automatically.
For example,
class EventSource:
def __init__(self, event_name, event_mgr, max_number, type):
self._event_name = event_name
self._event_manager = event_mgr
self._type = type
self._max_number = max_number
def count(self):
global CUR_NUMBER
for i in range(self._max_number):
CUR_NUMBER = i + 1
if CUR_NUMBER == 4: # assume this is a error check function,if error occurs,it will send a error event,and hopefully this event can terminate the sub-thread which is exactly running current code
print("************ detect error occurred , this thread should be terminated immediately !")
errorEvent = Event("error", CUR_NUMBER, type_=EVENT_TYPE_2)
# self._event_manager.sendEvent(errorEvent)
break
print("************ main thread start:now start process {} - count : {}".format(self._event_name, CUR_NUMBER))
event = Event("test", CUR_NUMBER, type_=self._type)
self._event_manager.sendEvent(event)
time.sleep(1)
推荐阅读
- python-3.x - 使用列表推导与 for 循环递增变量时获得不同的结果
- python - 如何使 x 轴上的单词在 matplotlib 中从右到左出现?
- angular - Angular Ionic Side Drawer 不渲染
- java - Javafx 中屏幕之间的参数
- python - 如何从多订单模型中获取项目模型的总和?
- alphametic-question - 调查文本中的随机forloop?
- r - 如何根据两个向量的组合命名 R 中的列?
- c# - 读取文件签名并区分 zip 文件和 docx 文件
- python - 使用 Discord.py-Rewrite 保存所有服务器成员的数组
- android - 浮动操作按钮上的 stateListAnimator 不起作用