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 相关文章推荐
C#返回当前系统所有可用驱动器符号的方法
Apr 18 Python
详解Python中的条件判断语句
May 14 Python
对Python 文件夹遍历和文件查找的实例讲解
Apr 26 Python
Python读取本地文件并解析网页元素的方法
May 21 Python
python ddt数据驱动最简实例代码
Feb 22 Python
Pandas_cum累积计算和rolling滚动计算的用法详解
Jul 04 Python
pyspark 随机森林的实现
Apr 24 Python
实现ECharts双Y轴左右刻度线一致的例子
May 16 Python
python logging模块的使用详解
Oct 23 Python
Django与AJAX实现网页动态数据显示的示例代码
Feb 24 Python
如何用python反转图片,视频
Apr 24 Python
如何用python清洗文件中的数据
Jun 18 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微信公众开发之获取周边酒店信息的方法
2014/12/22 PHP
PHP魔术方法使用方法汇总
2016/02/14 PHP
PHP浮点比较大小的方法
2016/02/14 PHP
分享5个非常有用的Laravel Blade指令
2018/05/30 PHP
php使用json-schema模块实现json校验示例
2019/09/28 PHP
日期函数扩展类Ver0.1.1
2006/09/07 Javascript
jQuery表格列宽可拖拽改变且兼容firfox
2014/09/03 Javascript
JavaScript中number转换成string介绍
2014/12/31 Javascript
JQuery实现防止退格键返回的方法
2015/02/12 Javascript
js查看一个函数的执行时间实例代码
2015/09/12 Javascript
理解AngularJs指令
2015/12/10 Javascript
再次谈论React.js实现原生js拖拽效果引起的一系列问题
2016/04/03 Javascript
AngularJs定制样式插入到ueditor中的问题小结
2016/08/01 Javascript
jQuery搜索框效果实现代码(百度关键词联想)
2021/02/25 Javascript
CSS+jQuery实现简单的折叠菜单
2016/12/20 Javascript
jQuery点击弹出层弹出模态框点击模态框消失代码分享
2017/01/21 Javascript
jQuery validate 验证radio实例
2017/03/01 Javascript
详解nodejs爬虫程序解决gbk等中文编码问题
2017/04/06 NodeJs
vue中component组件的props使用详解
2017/09/04 Javascript
基于Vue中点击组件外关闭组件的实现方法
2018/03/06 Javascript
如何使用VuePress搭建一个类型element ui文档
2019/02/14 Javascript
node中使用log4js4.x版本记录日志的方法
2019/08/20 Javascript
微信小程序 动态修改页面数据及参数传递过程详解
2019/09/27 Javascript
jsonp格式前端发送和后台接受写法的代码详解
2019/11/07 Javascript
从0搭建vue-cli4脚手架
2020/06/17 Javascript
[58:58]2018DOTA2亚洲邀请赛 4.4 淘汰赛 TNC vs VG 第二场
2018/04/05 DOTA
python3 破解 geetest(极验)的滑块验证码功能
2018/02/24 Python
Python 实现使用空值进行赋值 None
2020/03/12 Python
TensorFlow打印输出tensor的值
2020/04/19 Python
浏览器实现移动端高性能css3动画(开启gpu加速)
2013/12/23 HTML / CSS
全球领先的全景影像品牌:Insta360
2019/08/21 全球购物
镇党委书记群众路线整改措施思想汇报
2014/10/13 职场文书
2019餐饮行业创业计划书!
2019/06/27 职场文书
详解MongoDB的条件查询和排序
2021/06/23 MongoDB
MySQL的存储函数与存储过程的区别解析
2022/04/08 MySQL
Windows Server 2008配置防火墙策略详解
2022/06/28 Servers