我经常被问到如何杀死一个后台线程,这个问题的答案让很多人不开心: 线程是杀不死的。在本文中,我将向您展示 Python
中用于终止线程的两个选项。
如果我们是一个好奇宝宝的话,可能会遇到这样一个问题,就是:如何杀死一个 Python
的后台线程呢?我们可能尝试解决这个问题,却发现线程是杀不死的。而本文中将展示,在 Python
中用于终止线程的两个方式。
A Threaded Example
import random
import threading
import time
def bg_thread():
for i in range(1, 30):
print(f'{i} of 30 iterations...')
time.sleep(random.random()) # do some work...
print(f'{i} iterations completed before exiting.')
th = threading.Thread(target=bg_thread)
th.start()
th.join()
7
次迭代时,按下 Ctrl-C
来中断程序,发现后台运行的程序并没有终止掉。而在第 13
次迭代时,再次按下 Ctrl-C
来中断程序,发现程序真的退出了。$ python thread.py
1 of 30 iterations...
2 of 30 iterations...
3 of 30 iterations...
4 of 30 iterations...
5 of 30 iterations...
6 of 30 iterations...
7 of 30 iterations...
^CTraceback (most recent call last):
File "thread.py", line 14, in <module>
th.join()
File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1011, in join
self._wait_for_tstate_lock()
File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1027, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt
8 of 30 iterations...
9 of 30 iterations...
10 of 30 iterations...
11 of 30 iterations...
12 of 30 iterations...
13 of 30 iterations...
^CException ignored in: <module 'threading' from '/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py'>
Traceback (most recent call last):
File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1388, in _shutdown
lock.acquire()
KeyboardInterrupt:
Daemon Threads
Python
退出之前,它会等待任何非守护线程的线程。而守护线程就是,一个不会阻止 Python
解释器退出的线程。daemon
属性,可以在启动线程之前将这个属性设置为 True
,然后该线程就会被视为一个守护线程。下面是上面的示例应用程序,修改后守护线程版本:import random
import threading
import time
def bg_thread():
for i in range(1, 30):
print(f'{i} of 30 iterations...')
time.sleep(random.random()) # do some work...
print(f'{i} iterations completed before exiting.')
th = threading.Thread(target=bg_thread)
th.daemon = True
th.start()
th.join()
Ctrl-C
后进程立即就退出了。~ $ python x.py
1 of 30 iterations...
2 of 30 iterations...
3 of 30 iterations...
4 of 30 iterations...
5 of 30 iterations...
6 of 30 iterations...
^CTraceback (most recent call last):
File "thread.py", line 15, in <module>
th.join()
File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1011, in join
self._wait_for_tstate_lock()
File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1027, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt
Python
进程终止并返回到操作系统。这时,线程就不存在了。你可能认为这实际上是一种杀死线程的方法,但要考虑到以这种方式杀死线程,你必须同时杀死进程。Python Events
Event
对象。Event
类是由 Python
标准库的线程模块提供,你可以通过实例化类来创建一个事件对象,就像下面这个样子:
exit_event = threading.Event()
Event
对象可以处于两种状态之一: set
或 not set
。当我们实例化创建之后,默认事件并没有被设置。set
,则可以调用 set()
方法;is_set()
方法,设置了则返回 True
;wait()
方法等待事件,等待操作阻塞直到设置事件(可以设置超时)Ctrl-C
中断的信号处理程序,而不是突然退出,只需设置事件并让线程优雅地结束。import random
import signal
import threading
import time
exit_event = threading.Event()
def bg_thread():
for i in range(1, 30):
print(f'{i} of 30 iterations...')
time.sleep(random.random()) # do some work...
if exit_event.is_set():
break
print(f'{i} iterations completed before exiting.')
def signal_handler(signum, frame):
exit_event.set()
signal.signal(signal.SIGINT, signal_handler)
th = threading.Thread(target=bg_thread)
th.start()
th.join()
$ python thread.py
1 of 30 iterations...
2 of 30 iterations...
3 of 30 iterations...
4 of 30 iterations...
5 of 30 iterations...
6 of 30 iterations...
7 of 30 iterations...
^C7 iterations completed before exiting.
event
对象也是可以等待的:for i in range(1, 30):
print(f'{i} of 30 iterations...')
time.sleep(random.random())
if exit_event.is_set():
break
time.sleep()
的调用,这将阻塞线程。如果在线程 sleep
时设置了退出事件,那么它就不能检查事件的状态,因此在线程能够退出之前会有一个小的延迟。在这种情况下,如果有 sleep
,使用 wait()
方法将 sleep
与 event
对象的检查结合起来会更有效: for i in range(1, 30):
print(f'{i} of 30 iterations...')
if exit_event.wait(timeout=random.random()):
break
sleep
,因为在线程停留在 wait()
调用的中间时设置了事件,那么等待将立即返回。Conclusion
Python
中的 event
对象吗?它们是比较简单的同步原语之一,不仅可以用作退出信号,而且在线程需要等待某些外部条件发生的许多其他情况下也可以使用。本文分享自 Python数据分析实例 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!