简体   繁体   English

使用事件系统时如何正确杀死/退出/停止线程?

[英]How to kill/exit/stop thread properly while using event system?

I am creating a gui tool by python.我正在通过 python 创建一个 gui 工具。 Here comes a question that when I triggered a sub-thread to run some logic code by clicking on a button.The processing time maybe very long and some error maybe occurred during the process.And if error occurs I will send a error event to nofify the EventManager and call some function to kill/exit the sub-thread because its non-sense to continully run the left logic code.这里有一个问题,当我通过点击一个按钮触发一个子线程运行一些逻辑代码时。处理时间可能很长,并且在这个过程中可能会发生一些错误。如果发生错误我会发送一个错误事件来通知EventManager 并调用一些 function 来杀死/退出子线程,因为它没有意义继续运行左侧逻辑代码。 But I dont know how to kill/exit/stop the sub-thread properly using the error event.Could somebody help me plz?但我不知道如何使用错误事件正确杀死/退出/停止子线程。有人可以帮我吗?

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()

Hopefully the console will give me the logs as following:希望控制台会给我以下日志:

************ 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.count function 中,您可以在想要退出时简单地跳出循环。 If the function finishes running, the thread will exit automatically.如果 function 运行结束,线程将自动退出。

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM