如何在Python中实现守护进程可停止的轮询线程?

时间:2015-10-27 10:37:50

标签: python multithreading python-2.7

根据我的理解,python stdlib中没有这样的开箱即用的解决方案。

解决方案必须具备以下特征:

  • 使用目标函数和可选参数
  • 启动线程作为守护程序
  • 进行轮询:线程应每隔X秒重新运行一次目标
  • 允许轻松优雅的停止:中途不破坏目标执行
  • 公开从其所属程序外部停止的能力,特别是能够阻止线程测试代码。
  • 允许线程在停止后重新启动(或使用新线程进行伪重启)

我在SO上遇到了一些建议,但是想在这里汇总所有收集到的知识(我会在后续回答中做出贡献),以便听取任何新的或替代的或额外的想法。

1 个答案:

答案 0 :(得分:0)

我的提案使用threading库,因为它的广告级别高于thread

中间立场是这个解决方案,从其他SO回答中找到:

def main():
   t_stop= threading.Event()
   t = threading.Thread(target=thread, args=(1, t_stop))
   t.daemon = True
   t.start()

   time.sleep(duration)
   #stop the thread
   t_stop.set()

def thread(arg, stop_event):
    while(not stop_event.is_set()):
        # Code to execute here
        stop_event.wait(time)

遗憾的是,这需要我们在测试时使用t_stop对象 - 以便停止线程 - 并且对象的句柄不会被暴露。

解决方案是在某个顶级或全局字典中添加tt_stop句柄,以便测试代码到达。

另一种解决方案(从某处复制和改进)是使用以下内容:

def main():
    t = DaemonStoppableThread(sleep_time, target=target_function,
                              name='polling_thread',
                              args=(arg1, arg2))
    t.start()

# Stopping code from a test
def stop_polling_threads():
    threads = threading.enumerate()
    polling_threads = [thread for thread in threads
                       if 'polling_thread' in thread.getName()]
    for poll_thread in polling_threads:
        poll_thread.stop()


class DaemonStoppableThread(threading.Thread):

    def __init__(self, sleep_time, target=None,  **kwargs):
        super(DaemonStoppableThread, self).__init__(target=target, **kwargs)
        self.setDaemon(True)
        self.stop_event = threading.Event()
        self.sleep_time = sleep_time
        self.target = target

    def stop(self):
        self.stop_event.set()

    def stopped(self):
        return self.stop_event.isSet()

    def run(self):
        while not self.stopped():
            if self.target:
                self.target()
            else:
                raise Exception('No target function given')
            self.stop_event.wait(self.sleep_time)

与这些解决方案一样好,它们都不会面临重新启动轮询目标功能。

我避免使用表达式"重新启动线程",因为我知道python线程无法重新启动,所以必须使用新线程来允许这个"伪重启&#34 ;

编辑:

为了改进上述内容,多次启动/停止轮询目标的解决方案:

class ThreadManager(object):
    def __init__(self):
       self.thread = None

    def start_thread(self):
        if not self.thread or not self.thread.is_alive():
            self.thread = DaemonStoppableThread(sleep_time=5, target=some_func, args=(1, 2))
            self.thread.start()
        return 'thread running'

    def stop_thread(self):
        if self.thread and self.thread.is_alive():
            self.thread.stop()
            return 'thread stopped'
        else:
            return 'dead thread'

    def check_thread(self):
        if self.thread and self.thread.is_alive():
            return 'thread alive'
        else:
            return 'dead_thread'