本文转载自PHP中文网

前言:

不要试图用强制方法杀掉一个python线程,这从服务设计上就存在不合理性。 多线程本用来任务的协作并发,如果你使用强制手段干掉线程,那么很大几率出现意想不到的bug。 请记住一点,锁资源不会因为线程退出而释放锁资源 !

我们可以举出两个常见的例子:

1. 有个A线程拿到了锁,因为他是被强制干掉的,没能及时的release()释放锁资源,那么导致所有的线程获取资源是都被阻塞下去,这就是典型的死锁场景。

2.在常见的生产消费者的场景下,消费者从任务队列获取任务,但是被干掉后没有把正在做的任务丢回队列中,那么这就造成了数据丢失。

下面是java和python终止线程的方法:

java有三种方法可以使终止线程:

1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

2. 使用stop方法强行终止线程(不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。

3. 使用interrupt方法中断线程。

python可以有两种方法:

1. 退出标记

2. 使用ctypes强行杀掉线程

不管是python还是java环境下,理想的停止退出线程方法是 让线程自个自杀,所谓的线程自杀就是 你给他一个标志位,他退出线程。

下面我们会采用多种方法来测试 停止python线程的异常情况。我们查看一个进程所有的执行线程, 进程是用过掌控资源,线程是用作调度单元,进程要被调度执行必须要有一个线程,默认的线程和进程的pid一样的。

ps -mp 31449 -o THREAD,tid

USER %CPU PRI SCNT WCHAN USER SYSTEM TID

root 0.0 - - - - - -

root 0.0 19 - poll_s - - 31449

root 0.0 19 - poll_s - - 31450

获取到了进程所有的线程后,通过strace得知 31450 是需要我们kill的线程id,当我们kill的时候,会出现整个进程都崩溃的情况。 在多线程环境下,产生的信号是传递给整个进程的,一般而言,所有线程都有机会收到这个信号,进程在收到信号的的线程上下文执行信号处理函数,具体是哪个线程执行的难以获知。也就是说,信号会随机发个该进程的一个线程。

strace -p 31450 Process 31450 attached - interrupt to quit

select(0, NULL, NULL, NULL, {0, 320326}) = 0 (Timeout)

select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)

select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)

select(0, NULL, NULL, NULL, {1, 0}) = ? ERESTARTNOHAND (To be restarted)

--- SIGTERM (Terminated) @ 0 (0) ---

Process 31450 detached

上面出现的问题其实跟pthread的说明是一致的。当我们在python代码里加入 signal 信号处理函数后,回调函数可以防止整个进程的退出,那么问题来了,通过信号函数不能识别你要干掉哪一个线程,也就是说,不能精准的干掉某个线程。你虽然把信号发给31450线程id,但是信号受理人是所属进程的任何一个,另外传给信号处理函数的参数只有信号数和信号stack而已,可有可无的。

加了信号处理后,不会退出进程

select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)

select(0, NULL, NULL, NULL, {1, 0}) = ? ERESTARTNOHAND (To be restarted)

--- SIGTERM (Terminated) @ 0 (0) ---

rt_sigreturn(0xffffffff) = -1 EINTR (Interrupted system call)

select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)

select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)

如果想从外部通知杀掉某个线程,那么可以构建使用rpc服务,或者别的方式通信,signal信号不可以,因为无法无法传递更多的信息。

python的线程不是模拟的,是真实的内核线程,内核调用pthread方法,但Python上层没有提供关闭线程的方法,这就需要我们自己把握了。强烈推荐使用 event 或者 自定义标志位的方法, 如果非要强制杀掉线程,那么可以用python ctypes PyThreadState SetAsyncExc 方法强制退出,这样对于运行的python服务没有什么影响。

该函数的实现原理比较简单,其实也是在python虚拟机里做个标示位,然后由虚拟机运行一个异常来取消线程,虚拟机会帮你做好try cache。 切记不要在外部杀掉python的某个线程,虽然你能通过ctypes找到线程id,但是你直接kill会干掉整个进程的。

下面的代码是 用ctypes 杀掉线程的样例,不推荐使用,因为太粗暴了.

import ctypes

def terminate_thread(thread):

if not thread.isAlive():

return

exc = ctypes.py_object(SystemExit)

res = ctypes.pythonapi.PyThreadState_SetAsyncExc(

ctypes.c_long(thread.ident), exc)

if res == 0:

raise ValueError("nonexistent thread id")

elif res > 1:

ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)

raise SystemError("PyThreadState_SetAsyncExc failed")

咱们简单look一下PyThreadState源代码,总而言之触发线程的异常模式。 有兴趣的人可以阅读 python pystate.c 的设计,配合着youtube的一些视频分享。

int

PyThreadState_SetAsyncExc(long id, PyObject *exc) {
PyInterpreterState *interp = GET_INTERP_STATE();

...

HEAD_LOCK();

for (p = interp->tstate_head; p != NULL; p = p->next) {
if (p->thread_id == id) {
从链表里找到线程的id,避免死锁,我们需要释放head_mutex。

PyObject *old_exc = p->async_exc;

Py_XINCREF(exc); #增加该对象的引用数

p->async_exc = exc; # 更为exc模式

HEAD_UNLOCK();

Py_XDECREF(old_exc); # 因为要取消,当然也就递减引用

...

return 1; #销毁线程成功

}

}

HEAD_UNLOCK();

return 0;

}

原生posix pthread 可以使用 ptread_cancel(tid) 在主线程中结束子线程。但是 Python 的线程库不支持这样做,理由是我们不应该强制地结束一个线程,这样会带来很多隐患,应该让该线程自己结束自己。所以在 Python 中,推荐的方法是在子线程中循环判断一个标志位,在主线程中改变该标志位,子线程读到标志位改变,就结束自己。

类似这个逻辑:

def consumer_threading():

t1_stop= threading.Event()

t1 = threading.Thread(target=thread1, args=(1, t1_stop))

t2_stop = threading.Event()

t2 = threading.Thread(target=thread2, args=(2, t2_stop))

time.sleep(duration)

#stop the thread2

t2_stop.set()

def thread1(arg1, stop_event):

while(not stop_event.is_set()):

#similar to time.sleep()

stop_event.wait(time)

pass

def thread2(arg1, stop_event):

while(not stop_event.is_set()):

stop_event.wait(time)

pass

简单的总结,虽然我们可以用ctypes里的pystats来控制线程,但这种粗暴中断线程的方法是不合理的。 请选用 自杀模式 !如果你的线程正在发生io阻塞,而不能判断事件怎么办? 你的程序需要做优化了,最少在网络io层需要有主动的timeout,避免一直的阻塞下去。

ctype方法不推荐使用!强推Event和标志位的方式杀掉线程!

python杀死线程的三种方法相关推荐

  1. python调用cmd命令释放端口_详解python调用cmd命令三种方法

    目前我使用到的python中执行cmd的方式有三种 使用os.system("cmd") 该方法在调用完shell脚本后,返回一个16位的二进制数,低位为杀死所调用脚本的信号号码, ...

  2. Python创建多线程的三种方法

    Python创建多线程的三种方法 thread模块函数式创建线程 继承threading类创建多线程 threading模块函数式创建线程 使用总结 thread模块函数式创建线程 调用thread模 ...

  3. 测试Python下载图片的三种方法

    简 介: 通过Python软件包对网络URL图片链接进行下载,可以加快后期处理.本文测试了urllib, request两个软件包对图片进行下载效果.如果图片原网页有了防止下载机制,是无法下载图片. ...

  4. python学习音频-详解python播放音频的三种方法

    第一种 使用pygame模块 pygame.mixer.init() pygame.mixer.music.load(self.wav_file) pygame.mixer.music.set_vol ...

  5. python可以播放音乐吗_详解python播放音频的三种方法

    第一种 使用pygame模块 pygame.mixer.init() pygame.mixer.music.load(self.wav_file) pygame.mixer.music.set_vol ...

  6. java 终止方法_Java中终止线程的三种方法

    Java中终止线程的三种方法 Thread.stop, Thread.suspend, Thread.resume 和Runtime.runFinalizersOnExit 这些终止线程运行的方法已经 ...

  7. Java创建线程的三种方法

    这里不会贴代码,只是将创建线程的三种方法做个笼统的介绍,再根据源码添加上自己的分析. 通过三种方法可以创建java线程: 1.继承Thread类. 2.实现Runnable接口. 3.实现Callab ...

  8. Java 创建线程的三种方法比较

    在学习编程的过程中,我觉得不止要获得课本的知识,更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方,本文主要讲述Java 创建线程的三种方法比较, 更多Java专业知识,广州疯狂jav ...

  9. python求平方根的三种方法

    python求平方根的三种方法 题干描述 题目解答 题干描述 没啥好说的qwq,求根号下x,并舍弃小数部分,只保留整数 题目解答 方法一:不多bb,直接0.5次方(这应该是最没有营养的解法,面试官估计 ...

最新文章

  1. 大学计算机专业副修课,计算机学院举行本科课程教学大纲修订工作研讨会
  2. 为什么你写的代码糟透了?
  3. DOM相关方法,属性整理
  4. 悲报, GIF 之父因新冠去世
  5. java给qq发消息_QQ发送消息
  6. 脑洞大开!拿Transformer和CNN比较!犯错都像人类
  7. Struts与Ajax页面交互
  8. 【优化算法】学生心理学优化算法(SPBO)【含Matlab源码 1430期】
  9. 智能家居APP原型设计(附下载链接)—基于物联网的终端设备设计研究—Axure9高保真原型设计
  10. 【软件构造】过程与配置管理
  11. python实现车牌识别系统
  12. 中安证件识别系统介绍
  13. 个人主页网页设计模板
  14. 求两个数最大公因数(直接求、辗转相除法)、最小公倍数
  15. Nuke对图片添加Alpha通道
  16. vue下拉框数据填充
  17. Nature | 易基因DNA甲基化测序助力人多能干细胞向胚胎全能8细胞的人工诱导
  18. 其他设备android显示感叹号,设备管理器出现其它设备未知设备感叹号的解决方法!...
  19. Mac的常用快捷键(包括数学字符)
  20. SQL(之一)-SQL经典题目

热门文章

  1. 我看《肖申克的救赎》
  2. Jenkins实现发送邮件
  3. pyinstaller打包exe免杀和逆向浅析
  4. linux指纹登录实现原理,指纹识别技术原理与基于Linux系统的指纹识别门禁系统设计...
  5. 「超强」ChatGPT撰写的软件定制开发行业可行性报告分析
  6. 2014年秋浙大远程教育计算机离线作业.计算机基础知识题2,2014春浙大远程计算机应用基础第4次作业4.Excel知识题.doc...
  7. 金莹江苏省计算机学会教授,2020年江苏省大学生程序设计大赛在我校举办
  8. 计算机病毒不可侵入,计算机病毒考试题型.doc
  9. 《入门练习》1、长方形周长和面积
  10. windows7 关闭和启用IPV6隧道适配器