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 相关文章推荐
17个Python小技巧分享
Jan 23 Python
Python中的进程分支fork和exec详解
Apr 11 Python
wxPython中listbox用法实例详解
Jun 01 Python
Python 常用 PEP8 编码规范详解
Jan 22 Python
python获取网页中所有图片并筛选指定分辨率的方法
Mar 31 Python
python+flask实现API的方法
Nov 21 Python
对Python3 pyc 文件的使用详解
Feb 16 Python
Python+OpenCV采集本地摄像头的视频
Apr 25 Python
python异常处理try except过程解析
Feb 03 Python
python实现查找所有程序的安装信息
Feb 18 Python
Python之Sklearn使用入门教程
Feb 19 Python
Python 里最强的地图绘制神器
Mar 01 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
域名查询代码公布
2006/10/09 PHP
一步一步学习PHP(7) php 字符串相关应用
2010/03/05 PHP
一些PHP Coding Tips(php小技巧)[2011/04/02最后更新]
2011/05/02 PHP
用 Composer构建自己的 PHP 框架之设计 MVC
2014/10/30 PHP
PHP扩展程序实现守护进程
2015/04/16 PHP
盘点PHP和ASP.NET的10大对比!
2015/12/24 PHP
ThinkPHP连接Oracle数据库
2016/04/22 PHP
PHP实现支持加盐的图片加密解密
2016/09/09 PHP
PHP+mysql实现从数据库获取下拉树功能示例
2017/01/06 PHP
PHP那些琐碎的知识点(整理)
2017/05/20 PHP
使用 PHP Masked Package 屏蔽敏感数据的实现方法
2019/10/15 PHP
js与jquery中获取当前鼠标的x、y坐标位置的代码
2011/05/23 Javascript
利用window.name实现windowStorage代码分享
2014/01/02 Javascript
jQuery中before()方法用法实例
2014/12/25 Javascript
深入理解JavaScript系列(34):设计模式之命令模式详解
2015/03/03 Javascript
JS使用正则表达式除去字符串中重复字符的方法
2015/11/05 Javascript
jquery表单验证需要做些什么
2015/11/17 Javascript
用jQuery实现优酷首页轮播图
2017/01/09 Javascript
vue使用一些外部插件及样式的配置代码
2019/11/18 Javascript
javascript设计模式 ? 适配器模式原理与应用实例分析
2020/04/13 Javascript
[03:48]大碗DOTA
2019/07/25 DOTA
python获取各操作系统硬件信息的方法
2015/06/03 Python
python win32 简单操作方法
2017/05/25 Python
python pandas 组内排序、单组排序、标号的实例
2018/04/12 Python
Python数据可视化:幂律分布实例详解
2019/12/07 Python
在tensorflow实现直接读取网络的参数(weight and bias)的值
2020/06/24 Python
联想新西兰官方网站:Lenovo New Zealand
2018/10/30 全球购物
山海经纬软件测试笔试题和面试题
2013/04/02 面试题
老师推荐信
2013/10/28 职场文书
应届生财务会计求职信
2013/11/05 职场文书
高中班长自我鉴定
2013/12/20 职场文书
公司门卫的岗位职责
2014/02/19 职场文书
工作会议方案
2014/05/21 职场文书
网上祭先烈心得体会
2014/09/01 职场文书
培养联系人考察意见
2015/06/01 职场文书
2016年会领导致辞稿
2015/07/29 职场文书