千锋教育-做有情怀、有良心、有品质的职业教育机构

400-811-9990
手机站
千锋教育

千锋学习站 | 随时随地免费学

千锋教育

扫一扫进入千锋手机站

领取全套视频
千锋教育

关注千锋学习站小程序
随时随地免费学习课程

上海
  • 北京
  • 郑州
  • 武汉
  • 成都
  • 西安
  • 沈阳
  • 广州
  • 南京
  • 深圳
  • 大连
  • 青岛
  • 杭州
  • 重庆
当前位置:西安千锋IT培训  >  技术干货  >  如何杀死一个Python线程

如何杀死一个Python线程

来源:千锋教育
发布人:xqq
时间: 2023-11-12 00:31:35

我经常被问到如何杀死一个后台线程,这个问题的答案让很多人不开心:线程是杀不死的。在本文中,我将向您展示Python中用于终止线程的两个选项。

如果我们是一个好奇宝宝的话,可能会遇到这样一个问题,就是:如何杀死一个Python的后台线程呢?我们可能尝试解决这个问题,却发现线程是杀不死的。而本文中将展示,在Python中用于终止线程的两个方式。

1.线程无法结束

·AThreadedExample

下面是一个简单的,多线程的示例代码。

importrandom

importthreading

importtime

defbg_thread():

foriinrange(1,30):

print(f'{i}of30iterations...')

time.sleep(random.random())#dosomework...

print(f'{i}iterationscompletedbeforeexiting.')

th=threading.Thread(target=bg_thread)

th.start()

th.join()

使用下面命令来运行程序,在下面的程序运行中,当跑到第7次迭代时,按下Ctrl-C来中断程序,发现后台运行的程序并没有终止掉。而在第13次迭代时,再次按下Ctrl-C来中断程序,发现程序真的退出了。

$pythonthread.py

1of30iterations...

2of30iterations...

3of30iterations...

4of30iterations...

5of30iterations...

6of30iterations...

7of30iterations...

^CTraceback(mostrecentcalllast):

File"thread.py",line14,in

th.join()

File"/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py",line1011,injoin

self._wait_for_tstate_lock()

File"/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py",line1027,in_wait_for_tstate_lock

eliflock.acquire(block,timeout):

KeyboardInterrupt

8of30iterations...

9of30iterations...

10of30iterations...

11of30iterations...

12of30iterations...

13of30iterations...

^CExceptionignoredin:

Traceback(mostrecentcalllast):

File"/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py",line1388,in_shutdown

lock.acquire()

KeyboardInterrupt:

这很奇怪,不是吗?究其原因是,Python有一些逻辑是会在进程退出前运行的,专门用来等待任何没有被配置为守护线程的后台线程结束,然后再把控制权真正交给操作系统。因此,该进程在其主线程运行时收到到了中断信号,并准备退出。首先,它需要等待后台线程运行结束。但是,这个线程对中断一无所知,这个线程只知道它需要在运行结束前完成30次迭代。

Python在退出过程中使用的等待机制有一个规定,当收到第二个中断信号时,就会中止。这就是为什么第二个Ctrl-C会立即结束进程。所以我们看到了,线程是不能被杀死!在下面的章节中,将向展示Python中的两个方式,来使线程及时结束。

2.使用守护进程

·DaemonThreads

在上面提到过,在Python退出之前,它会等待任何非守护线程的线程。而守护线程就是,一个不会阻止Python解释器退出的线程。

如何使一个线程成为一个守护线程?所有的线程对象都有一个daemon属性,可以在启动线程之前将这个属性设置为True,然后该线程就会被视为一个守护线程。下面是上面的示例应用程序,修改后守护线程版本:

importrandom

importthreading

importtime

defbg_thread():

foriinrange(1,30):

print(f'{i}of30iterations...')

time.sleep(random.random())#dosomework...

print(f'{i}iterationscompletedbeforeexiting.')

th=threading.Thread(target=bg_thread)

th.daemon=True

th.start()

th.join()

再次运行它,并尝试中断它,发现第一个执行Ctrl-C后进程立即就退出了。

~$pythonx.py

1of30iterations...

2of30iterations...

3of30iterations...

4of30iterations...

5of30iterations...

6of30iterations...

^CTraceback(mostrecentcalllast):

File"thread.py",line15,in

th.join()

File"/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py",line1011,injoin

self._wait_for_tstate_lock()

File"/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py",line1027,in_wait_for_tstate_lock

eliflock.acquire(block,timeout):

KeyboardInterrupt

那么这个线程会发生什么呢?线程继续运行,就像什么都没发生一样,直到Python进程终止并返回到操作系统。这时,线程就不存在了。你可能认为这实际上是一种杀死线程的方法,但要考虑到以这种方式杀死线程,你必须同时杀死进程。

3.使用事件对象

·PythonEvents

使用守护线程,是一种避免在多线程程序中处理意外中断的简单方法,但这是一种只在进程退出的特殊情况下才有效的技巧。不幸的是,有些时候,一个应用程序可能想结束一个线程而不必杀死自己。另外,有些线程可能需要在退出前执行清理工作,而守护线程则不允许这样操作。

那么,还有什么其他选择呢?既然不可能强制线程结束,那么唯一的选择就是给它添加逻辑,让它在被要求退出时自愿退出。有多种方法都可以解决上述问题,但我特别喜欢的一种方法,就是使用一个Event对象。

Event类是由Python标准库的线程模块提供,你可以通过实例化类来创建一个事件对象,就像下面这个样子:

exit_event=threading.Event()

Event对象可以处于两种状态之一:set或notset。当我们实例化创建之后,默认事件并没有被设置。

·若要将事件状态更改为set,则可以调用set()方法;

·要查明是否设置了事件,使用is_set()方法,设置了则返回True;

·还可以使用wait()方法等待事件,等待操作阻塞直到设置事件(可以设置超时)

其核心思路,就是在线程需要退出的时候设置事件。然后,线程需要经常地检查事件的状态(通常是在循环中),并在发现事件已经设置时处理自己的终止。对于上面显示的示例,一个好的解决方案是添加一个捕获Ctrl-C中断的信号处理程序,而不是突然退出,只需设置事件并让线程优雅地结束。

importrandom

importsignal

importthreading

importtime

exit_event=threading.Event()

defbg_thread():

foriinrange(1,30):

print(f'{i}of30iterations...')

time.sleep(random.random())#dosomework...

ifexit_event.is_set():

break

print(f'{i}iterationscompletedbeforeexiting.')

defsignal_handler(signum,frame):

exit_event.set()

signal.signal(signal.SIGINT,signal_handler)

th=threading.Thread(target=bg_thread)

th.start()

th.join()

如果你尝试中断这个版本的应用程序,一切看起来都会更好:

$pythonthread.py

1of30iterations...

2of30iterations...

3of30iterations...

4of30iterations...

5of30iterations...

6of30iterations...

7of30iterations...

^C7iterationscompletedbeforeexiting.

需要注意的是,中断是如何被优雅地处理的,以及线程能够运行在循环之后出现的代码。如果当线程需要在退出之前,关闭文件句柄或数据库连接时,这种方式就非常有用了。其能够在线程退出之前,运行清理代码有时是必要的,以避免资源泄漏。我在上面提到过,event对象也是可以等待的:

foriinrange(1,30):

print(f'{i}of30iterations...')

time.sleep(random.random())

ifexit_event.is_set():

break

在每个迭代中,都有一个对time.sleep()的调用,这将阻塞线程。如果在线程sleep时设置了退出事件,那么它就不能检查事件的状态,因此在线程能够退出之前会有一个小的延迟。在这种情况下,如果有sleep,使用wait()方法将sleep与event对象的检查结合起来会更有效:

foriinrange(1,30):

print(f'{i}of30iterations...')

ifexit_event.wait(timeout=random.random()):

break

这个解决方案有效地为提供了一个可中断的sleep,因为在线程停留在wait()调用的中间时设置了事件,那么等待将立即返回。

4.总结陈述说明

·Conclusion

你知道Python中的event对象吗?它们是比较简单的同步原语之一,不仅可以用作退出信号,而且在线程需要等待某些外部条件发生的许多其他情况下也可以使用。

以上内容为大家介绍了如何杀死一个Python线程,希望对大家有所帮助,如果想要了解更多Python相关知识,请关注IT培训机构:千锋教育。http://www.mobiletrain.org/

声明:本站稿件版权均属千锋教育所有,未经许可不得擅自转载。

猜你喜欢LIKE

python怎么写个简单的python脚本

2023-11-12

python在哪里能输入

2023-11-12

python怎么换行

2023-11-12

最新文章NEW

如何中断python的执行

2023-11-12

Python元组怎么转换成列表

2023-11-12

python列表是否允许重复

2023-11-12

相关推荐HOT

更多>>

快速通道 更多>>

最新开班信息 更多>>

网友热搜 更多>>