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回溯法实现数组全排列输出实例分析
Mar 17 Python
Python简单实现enum功能的方法
Apr 25 Python
Python爬取网易云音乐热门评论
Mar 31 Python
Python使用django搭建web开发环境
Jun 09 Python
使用Python从零开始撸一个区块链
Mar 14 Python
Python基于whois模块简单识别网站域名及所有者的方法
Apr 23 Python
python得到电脑的开机时间方法
Oct 15 Python
Python实现元素等待代码实例
Nov 11 Python
Python笔记之观察者模式
Nov 20 Python
python将四元数变换为旋转矩阵的实例
Dec 04 Python
如何在mac版pycharm选择python版本
Jul 21 Python
Python实现提取PDF简历信息并存入Excel
Apr 02 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同时使用session和cookie来保存用户登录信息的实现代码
2016/05/13 PHP
PHP面向对象五大原则之单一职责原则(SRP)详解
2018/04/04 PHP
laravel使用数据库测试注意事项
2020/04/10 PHP
IE/FireFox具备兼容性的拖动代码
2007/08/13 Javascript
IE bug table元素的innerHTML
2010/01/11 Javascript
JavaScript中String和StringBuffer的速度之争
2010/04/01 Javascript
JS面向对象编程浅析
2011/08/28 Javascript
基于jQuery的JavaScript模版引擎JsRender使用指南
2014/12/29 Javascript
JavaScript中消除闭包的一般方法介绍
2015/03/16 Javascript
jQuery on()方法使用技巧详解
2015/04/16 Javascript
JavaScript对表格或元素按文本,数字或日期排序的方法
2015/05/26 Javascript
JavaScript位移运算符(无符号) &gt;&gt;&gt; 三个大于号 的使用方法详解
2016/03/31 Javascript
使用jQuery UI库开发Web界面的简单入门指引
2016/04/22 Javascript
一道面试题引发的对javascript类型转换的思考
2017/03/06 Javascript
jQuery Position方法使用和兼容性
2017/08/23 jQuery
vue-cli监听组件加载完成的方法
2018/09/07 Javascript
koa大型web项目中使用路由装饰器的方法示例
2019/04/02 Javascript
详解vue 命名视图
2019/08/14 Javascript
vue中添加与删除关键字搜索功能
2019/10/12 Javascript
Flask框架配置与调试操作示例
2018/07/23 Python
Django密码系统实现过程详解
2019/07/19 Python
flask 框架操作MySQL数据库简单示例
2020/02/02 Python
Python环境下安装PyGame和PyOpenGL的方法
2020/03/25 Python
详解Canvas事件绑定
2018/06/27 HTML / CSS
伦敦所有西区剧院演出官方票务代理:Theatre Tickets Direct
2017/05/26 全球购物
大学生就业自我鉴定
2013/10/26 职场文书
个人职业生涯规划书1500字
2013/12/31 职场文书
餐饮加盟计划书
2014/01/10 职场文书
书香家庭事迹材料
2014/05/09 职场文书
小学校长先进事迹材料
2014/05/13 职场文书
企业领导班子四风对照检查材料
2014/09/27 职场文书
群众路线领导班子四风对照检查材料
2014/09/27 职场文书
2014年反洗钱工作总结
2014/11/22 职场文书
雷锋电影观后感
2015/06/10 职场文书
PHP中strval()函数实例用法
2021/06/07 PHP
Redis基本数据类型Zset有序集合常用操作
2022/06/01 Redis