Python中用Ctrl+C终止多线程程序的问题解决


Posted in Python onMarch 30, 2013
#!/bin/env python
 # -*- coding: utf-8 -*-
 #filename: peartest.py import threading, signal
 is_exit = False
 def doStress(i, cc):
     global is_exit
     idx = i
     while not is_exit:
         if (idx < 10000000):
             print "thread[%d]: idx=%d"%(i, idx)
             idx = idx + cc
         else:
             break
     print "thread[%d] complete."%i
 def handler(signum, frame):
     global is_exit
     is_exit = True
     print "receive a signal %d, is_exit = %d"%(signum, is_exit)
 if __name__ == "__main__":
     signal.signal(signal.SIGINT, handler)
     signal.signal(signal.SIGTERM, handler)
     cc = 5
     for i in range(cc):
         t = threading.Thread(target=doStress, args=(i,cc))
         t.start()

上面是一个模拟程序,并不真正向服务发送请求,而代之以在一千万以内,每个线程每隔并发数个(cc个)打印一个整数。很明显,当所有线程都完成自己的任务后,进程会正常退出。但如果我们中途想退出(试想一个压力测试程序,在中途已经发现了问题,需要停止测试),该肿么办?你当然可以用ps查找到进程号,然后kill -9杀掉,但这样太繁琐了,捕捉Ctrl+C是最自然的想法。上面示例程序中已经捕捉了这个信号,并修改全局变量is_exit,线程中会检测这个变量,及时退出。

但事实上这个程序并不work,当你按下Ctrl+C时,程序照常运行,并无任何响应。网上搜了一些资料,明白是python的子线程如果不是daemon的话,主线程是不能响应任何中断的。但设为daemon后主线程会随之退出,接着整个进程很快就退出了,所以还需要在主线程中检测各个子线程的状态,直到所有子线程退出后自己才退出,因此上例29行之后的代码可以修改为:

threads=[]
     for i in range(cc):
         t = threading.Thread(target=doStress, args=(i, cc))
         t.setDaemon(True)
         threads.append(t)
         t.start()
     for i in range(cc):
         threads[i].join()

重新试一下,问题依然没有解决,进程还是没有响应Ctrl+C,这是因为join()函数同样会waiting在一个锁上,使主线程无法捕获信号。因此继续修改,调用线程的isAlive()函数判断线程是否完成:

while 1:
         alive = False
         for i in range(cc):
             alive = alive or threads[i].isAlive()
         if not alive:
             break

这样修改后,程序完全按照预想运行了:可以顺利的打印每个线程应该打印的所有数字,也可以中途用Ctrl+C终结整个进程。完整的代码如下:

#!/bin/env python
 # -*- coding: utf-8 -*-
 #filename: peartest.py import threading, signal
 is_exit = False
 def doStress(i, cc):
     global is_exit
     idx = i
     while not is_exit:
         if (idx < 10000000):
             print "thread[%d]: idx=%d"%(i, idx)
             idx = idx + cc
         else:
             break
     if is_exit:
         print "receive a signal to exit, thread[%d] stop."%i
     else:
         print "thread[%d] complete."%i
 def handler(signum, frame):
     global is_exit
     is_exit = True
     print "receive a signal %d, is_exit = %d"%(signum, is_exit)
 if __name__ == "__main__":
     signal.signal(signal.SIGINT, handler)
     signal.signal(signal.SIGTERM, handler)
     cc = 5
     threads = []
     for i in range(cc):
         t = threading.Thread(target=doStress, args=(i,cc))
         t.setDaemon(True)
         threads.append(t)
         t.start()
     while 1:
         alive = False
         for i in range(cc):
             alive = alive or threads[i].isAlive()
         if not alive:
             break

其实,如果用python写一个服务,也需要这样,因为负责服务的那个线程是永远在那里接收请求的,不会退出,而如果你想用Ctrl+C杀死整个服务,跟上面的压力测试程序是一个道理。总结一下,python多线程中要响应Ctrl+C的信号以杀死整个进程,需要:

1.把所有子线程设为Daemon;
2.使用isAlive()函数判断所有子线程是否完成,而不是在主线程中用join()函数等待完成;
3.写一个响应Ctrl+C信号的函数,修改全局变量,使得各子线程能够检测到,并正常退出。

Python 相关文章推荐
Python中使用copy模块实现列表(list)拷贝
Apr 14 Python
Python采用Django制作简易的知乎日报API
Aug 03 Python
Python+OpenCV实现车牌字符分割和识别
Mar 31 Python
python实现简单加密解密机制
Mar 19 Python
python3对接mysql数据库实例详解
Apr 30 Python
python删除文件夹下相同文件和无法打开的图片
Jul 16 Python
Python求正态分布曲线下面积实例
Nov 20 Python
django-csrf使用和禁用方式
Mar 13 Python
python 连续不等式语法糖实例
Apr 15 Python
小 200 行 Python 代码制作一个换脸程序
May 12 Python
python用tkinter实现一个gui的翻译工具
Oct 26 Python
详解利用python识别图片中的条码(pyzbar)及条码图片矫正和增强
Nov 17 Python
python利用hook技术破解https的实例代码
Mar 25 #Python
利用python获得时间的实例说明
Mar 25 #Python
python 将字符串转换成字典dict
Mar 24 #Python
使用python提取html文件中的特定数据的实现代码
Mar 24 #Python
python 切片和range()用法说明
Mar 24 #Python
python list中append()与extend()用法分享
Mar 24 #Python
python del()函数用法
Mar 24 #Python
You might like
php以fastCGI的方式运行时文件系统权限问题及解决方法
2015/05/11 PHP
php实现生成验证码实例分享
2016/04/10 PHP
浅谈PHP中类和对象的相关函数
2017/04/26 PHP
PHP开发实现快递查询功能详解
2019/04/08 PHP
Laravel 实现关系模型取出需要的字段
2019/10/10 PHP
Laravel框架Eloquent ORM删除数据操作示例
2019/12/03 PHP
通过ifame指向的页面高度调整iframe的高度
2006/10/05 Javascript
jquery中$each()方法的使用指南
2015/04/30 Javascript
JS中的forEach、$.each、map方法推荐
2016/04/05 Javascript
Chrome不支持showModalDialog模态对话框和无法返回returnValue问题的解决方法
2016/10/30 Javascript
JS 实现导航菜单中的二级下拉菜单的几种方式
2016/10/31 Javascript
JavaScript实现QQ聊天消息展示和评论提交功能
2017/05/22 Javascript
JavaScript正则表达式简单实用实例
2017/06/23 Javascript
AngularJS基于provider实现全局变量的读取和赋值方法
2017/06/28 Javascript
jQuery插件artDialog.js使用与关闭方法示例
2017/10/09 jQuery
几个你不知道的技巧助你写出更优雅的vue.js代码
2018/06/11 Javascript
详解小程序之简单登录注册表单验证
2019/05/13 Javascript
小程序实现锚点滑动效果
2019/09/23 Javascript
详解JavaScript中分解数字的三种方法
2021/01/05 Javascript
[01:56]《DOTA2》中文配音CG
2013/04/22 DOTA
[02:36]DOTA2英雄基础教程 一击致命幻影刺客
2013/12/06 DOTA
[04:46]2018年度玩家喜爱的电竞媒体-完美盛典
2018/12/16 DOTA
Python的requests网络编程包使用教程
2016/07/11 Python
Python爬取网易云音乐上评论火爆的歌曲
2017/01/19 Python
python数据类型判断type与isinstance的区别实例解析
2017/10/31 Python
关于python写入文件自动换行的问题
2018/06/23 Python
Django Celery异步任务队列的实现
2019/07/24 Python
nginx黑名单和django限速,最简单的防恶意请求方法分享
2019/08/09 Python
BONIA官方网站:国际奢侈品牌和皮革专家
2016/11/27 全球购物
手工制作的豪华英式沙发和沙发床:Willow & Hall
2019/05/03 全球购物
协议书模板
2014/04/23 职场文书
营销计划书范文
2015/01/17 职场文书
中标通知书范本
2015/04/17 职场文书
春季运动会加油词
2015/07/18 职场文书
Jupyter notebook 输出部分显示不全的解决方案
2021/04/24 Python
php将xml转化对象的实例详解
2021/11/17 PHP