请问在 Python 的事件系统中,如何可以通过事件通知立刻终结一个正在运行的子线程?
大家好,最近在完善手头上一个基于事件系统的 GUI 。
现在遇到一个问题,就是当我在执行一个按钮点击事件的时候,实际会开一个子线程进行业务逻辑的处理,这个处理过程可能会比较长,并且中间可能会出现一些不符合预期的情况发生,当发生这种情况时,我希望会借由发送一个错误的事件通知 EventManager,然后调用 listener 的方法来立刻终结这个对应的错误线程(因为之前已经发生了错误了,后续逻辑代码继续执行没有意义了)
代码如下,大佬们直接复制运行即可观察。如能解答,万分感谢!
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: # 在业务逻辑线程中增加检测环节,如果发生错误就会发送错误事件,希望可以立刻终结当前的线程,不执行后续的代码 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): # 在正式开始执行逻辑子线程之前,确实可以提前做一些判断,来决定是否满足条件,开始接下来的逻辑子线程,但是这个不在本次讨论范围内 es = EventSource("eventSource", eventMgr, max_number, EVENT_TYPE_1) self.taskThread = Thread(target=es.count, daemon=True).start() # 这里开始执行实际的业务逻辑子线程 def terminate_error_thread(self): # 这个方法在 GUIListener 接受到事件源发出的错误逻辑时被唤起,用来立刻终结正在执行事件源的线程,不做后续无用的代码逻辑处理 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 !
到这里就应该自然停止。