非强制终止python线程的方法介绍

本文给大家分享的是走着的一些强制杀掉python线程经验教训,如果你使用强制手段干掉线程,那么很大几率出现意想不到的bug。  请记住一点,锁资源不会因为线程退出而释放锁资源 !

前言:

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

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

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

立即学习“Python免费学习笔记(深入)”;

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  TIDroot   0.0  -  - -     -   -   -root   0.0 19  - poll_s  -   - 31449root   0.0 19  - poll_s  -   - 31450

登录后复制

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

strace -p 31450 Process 31450 attached - interrupt to quitselect(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的一些视频分享。

 intPyThreadState_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,避免一直的阻塞下去。

【相关推荐】

1. Python免费视频教程

2. Python基础入门教程

3. Python面向对象视频教程

以上就是非强制终止python线程的方法介绍的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2273244.html

(0)
上一篇 2025年2月27日 13:18:22
下一篇 2025年2月24日 06:25:36

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • Python多进程导入CSV至数据库

    本文给大家分享的是使用python实现多进程导入csv文件数据到mysql的思路方法以及具体的代码分享,有相同需求的小伙伴可以参考下 前段时间帮同事处理了一个把 CSV 数据导入到 MySQL 的需求。两个很大的 CSV 文件, 分别有 3…

    编程技术 2025年2月27日
    200
  • 详解防止sql注入的python方法

    sql注入是比较常见的网络攻击方式之一,它不是利用操作系统的bug来实现攻击,而是针对程序员编程时的疏忽,通过sql语句,实现无帐号登录,甚至篡改数据库。下面这篇文章主要给大家介绍了关于python中防止sql注入的方法,需要的朋友可以参考…

    编程技术 2025年2月27日
    200
  • python识别验证码的代码详解

    这篇文章主要介绍了python中识别验证码的相关资料,这属于学习python的基本入门教程,文中介绍的非常详细,文末也给出了完整的示例代码,需要的朋友们可以参考学习,下面来一起看看吧。 前言 验证码?我也能破解? 关于验证码的介绍就不多说了…

    2025年2月27日 编程技术
    200
  • 链接和操作 memcache的方法详解

    下面小编就为大家带来一篇python 链接和操作 memcache方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧 1,打开memcached服务 memcached -m 10 -p 12000 登录后复…

    编程技术 2025年2月27日
    200
  • 简述SQLAlchemy中排序的容易犯的一个错误

    这篇文章主要介绍了关于python中sqlalchemy排序的一个坑,文中给出了详细的示例代码,需要的朋友可以参考借鉴,感兴趣的朋友们下面来一起学习学习吧。 前言 SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数…

    编程技术 2025年2月27日
    200
  • 关于python函数中的参数详解

    昨天看《python核心编程》的时候,刚好看到了函数部分,于是顺势将目前接触到的集中参数类型都总结一下吧^^ (1)       位置参数,调用函数时按位置传入参数 (2)       默认参数,即在函数定义时就给出参数的值,设置默认参数时…

    编程技术 2025年2月27日
    200
  • Python读取文件后n行的代码示例

    这篇文章主要介绍了python实现读取文件最后n行的方法,涉及python针对文件的读取、遍历与运算相关操作技巧,需要的朋友可以参考下 # -*- coding:utf8-*-import osimport timeimport datet…

    编程技术 2025年2月27日
    200
  • Python中tcp socket编程的实例详解

    这篇文章主要介绍了python基础教程之tcp socket编程详解及简单实例的相关资料,需要的朋友可以参考下 Python tcp socket编程详解 初学脚本语言Python,测试可用的tcp通讯程序: 服务器: #!/usr/bin…

    编程技术 2025年2月27日
    200
  • oracle的安装及数据库连接的方法详解

    这篇文章主要介绍了python安装oracle扩展及数据库连接方法,较为详细的分析了python下载oracle扩展及windows、linux环境下的安装步骤、操作技巧及注意事项,需要的朋友可以参考下 本文实例讲述了python安装ora…

    编程技术 2025年2月27日
    100
  • 安装cx_Oracle会遇到的报错以及解决方案

    这篇文章主要介绍了python安装cx_oracle模块常见问题与解决方法,举例分析了python在windows平台与linux平台安装cx_oracle模块常见问题、解决方法及相关注意事项,需要的朋友可以参考下 本文实例讲述了pytho…

    编程技术 2025年2月27日
    200

发表回复

登录后才能评论