退出 python 线程的更多 pythonic 方法 - 守护程序与 stop_event

More pythonic way to exit a python thread - daemon vs stop_event

以下代码:

import threading
import time
from functools import partial
from itertools import count

def daemon_loop(sleep_interval, stop_event):
    for j in count():
        print(j)
        if stop_event.is_set():
            break
        time.sleep(sleep_interval)
        print('Slept %s' % sleep_interval)
    print('Prod terminating')

if __name__ == '__main__':
    stop_event = threading.Event() #
    target = partial(daemon_loop, sleep_interval=2, stop_event=stop_event)
    prod_thread = threading.Thread(target=target,
                                   # daemon=True
                                   )

    try:
        prod_thread.start()
        while True:
            time.sleep(10)
    except KeyboardInterrupt:
        print('Terminating...')
        stop_event.set()

在键盘中断时打印:

C:\Users\MrD\.PyCharm2018.2\config\scratches>c:\_\Python363-64\python.exe thread_daemon.py
0
Slept 2
1
Terminating...
Slept 2
2
Prod terminating

取消注释 # daemon=True 行会导致 prod_thread 立即结束:

C:\Users\MrD\.PyCharm2018.2\config\scratches>c:\_\Python363-64\python.exe thread_daemon.py
0
Slept 2
1
Terminating...

我的问题是 preferred/more pythonic 处理线程终止的方法是什么——我应该放弃事件机制并只将线程标记为守护进程还是我错过了一些边缘情况?

参见:

我做得还不够 Python 无法给你 "Pythonic" 答案,但我可以用更通用的编程术语来回答。

首先,我不喜欢终止线程。有 安全和正常的情况,比如你在这里的例子 - 但是在 print 中间终止写入它的输出会感觉有点脏。

其次,如果您想继续使用 sleep(我也不喜欢),您可以在 if stop_event.is_set():break 之后重复该睡觉了。 (不要移动代码,复制它。)在这种情况下 sleep 的主要问题是它会等待完整的 sleep_interval,即使在那段时间内设置了事件。

第三 - 我的偏好 - 而不是使用 sleep,对事件执行 wait 超时。如果等待期间没有设置事件,等待超时时间后waitreturnsfalse。如果事件 在等待 之前或期间设置, wait returns true 立即 (即中止超时,让您快速、干净地关闭线程。)

因此您的代码将如下所示:

def daemon_loop(sleep_interval, stop_event):
    for j in count():
        print(j)
        if stop_event.wait(sleep_interval):
            break
        print('Slept %s' % sleep_interval)
    print('Prod terminating')