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爬虫入门教程--优雅的HTTP库requests(二)
May 25 Python
python爬取淘宝商品详情页数据
Feb 23 Python
Python中Proxypool库的安装与配置
Oct 19 Python
Linux CentOS Python开发环境搭建教程
Nov 28 Python
Python字符串逆序的实现方法【一题多解】
Feb 18 Python
在Python中COM口的调用方法
Jul 03 Python
Python实现动态给类和对象添加属性和方法操作示例
Feb 29 Python
PyCharm Anaconda配置PyQt5开发环境及创建项目的教程详解
Mar 24 Python
基于Python把网站域名解析成ip地址
May 25 Python
Python数据可视化图实现过程详解
Jun 12 Python
Python中免验证跳转到内容页的实例代码
Oct 23 Python
python调用jenkinsAPI构建jenkins,并传递参数的示例
Dec 09 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笔记之常用文件操作
2010/10/12 PHP
php字符串截取的简单方法
2013/07/04 PHP
PHP中常用的字符串格式化函数总结
2014/11/19 PHP
php获取四位字母和数字的随机数的实现方法
2015/01/09 PHP
WordPress中调试缩略图的相关PHP函数使用解析
2016/01/07 PHP
PHP简单实现生成txt文件到指定目录的方法
2016/04/25 PHP
用javascript getComputedStyle获取和设置style的原理
2008/10/10 Javascript
Javascript 面向对象 继承
2010/05/13 Javascript
上传图片js判断图片尺寸和格式兼容IE
2014/09/01 Javascript
jQuery版AJAX简易封装代码
2016/09/14 Javascript
jQuery实现级联下拉框实战(5)
2017/02/08 Javascript
jQuery插件zTree实现删除树子节点的方法示例
2017/03/08 Javascript
简单明了区分escape、encodeURI和encodeURIComponent
2018/05/26 Javascript
vue项目中使用百度地图的方法
2018/06/08 Javascript
express框架中使用jwt实现验证的方法
2019/08/25 Javascript
微信小程序点击列表跳转到对应详情页过程解析
2019/09/26 Javascript
[01:29]2014DOTA2展望TI 剑指西雅图DK战队专访
2014/06/30 DOTA
python正常时间和unix时间戳相互转换的方法
2015/04/23 Python
常见的python正则用法实例讲解
2016/06/21 Python
Python正则表达式匹配中文用法示例
2017/01/17 Python
详解Python import方法引入模块的实例
2017/08/02 Python
numpy自动生成数组详解
2017/12/15 Python
攻击者是如何将PHP Phar包伪装成图像以绕过文件类型检测的(推荐)
2018/10/11 Python
python logging日志模块原理及操作解析
2019/10/12 Python
Python基于requests实现模拟上传文件
2020/04/21 Python
Python发送邮件实现基础解析
2020/08/14 Python
意大利奢侈品购物网站:Deliberti
2019/10/08 全球购物
Happy Socks英国官网:购买五颜六色的袜子
2020/11/03 全球购物
预备党员思想汇报范文
2014/01/11 职场文书
领导干部培训感言
2014/01/23 职场文书
材料员岗位职责
2014/03/13 职场文书
电子商务专业应届生求职信
2014/05/28 职场文书
技术经济专业求职信
2014/09/03 职场文书
大学入学感言
2015/08/01 职场文书
2016年五一国际劳动节活动总结
2016/04/06 职场文书
React 并发功能体验(前端的并发模式)
2021/07/01 Javascript