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使用socket进行简单网络连接的方法
Apr 29 Python
Python中字典的setdefault()方法教程
Feb 07 Python
Python自然语言处理之词干,词形与最大匹配算法代码详解
Nov 16 Python
Tensorflow实现卷积神经网络用于人脸关键点识别
Mar 05 Python
caffe binaryproto 与 npy相互转换的实例讲解
Jul 09 Python
Python3网络爬虫开发实战之极验滑动验证码的识别
Aug 02 Python
pytorch中的transforms模块实例详解
Dec 31 Python
解决Django部署设置Debug=False时xadmin后台管理系统样式丢失
Apr 07 Python
jupyter实现重新加载模块
Apr 16 Python
记一次python 爬虫爬取深圳租房信息的过程及遇到的问题
Nov 24 Python
Python基于mediainfo批量重命名图片文件
Dec 29 Python
python 爬取华为应用市场评论
May 29 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 foreach、while性能比较
2009/10/15 PHP
PHP 小心urldecode引发的SQL注入漏洞
2011/10/27 PHP
Zend的MVC机制使用分析(一)
2013/05/02 PHP
一个简单的php加密解密函数(动态加密)
2013/06/19 PHP
简单说说PHP优化那些事(经验分享)
2014/11/27 PHP
php简单图像创建入门实例
2015/06/10 PHP
php+redis实现注册、删除、编辑、分页、登录、关注等功能示例
2017/02/15 PHP
PHP实现的服务器一致性hash分布算法示例
2018/08/09 PHP
BOOM vs RR BO5 第三场 2.14
2021/03/10 DOTA
用window.location.href实现刷新另个框架页面
2007/03/07 Javascript
jQuery Validation实例代码 让验证变得如此容易
2010/10/18 Javascript
js 得到文件后缀(通过正则实现)
2013/07/08 Javascript
JavaScript实现将数组数据添加到Select下拉框的方法
2015/08/21 Javascript
详解基于Bootstrap扁平化的后台框架Ace
2015/11/27 Javascript
AngularJS中watch监听用法分析
2016/11/04 Javascript
微信小程序仿美团城市选择
2018/06/06 Javascript
详解ES6 系列之异步处理实战
2018/10/26 Javascript
vue slot与传参实例代码讲解
2019/04/28 Javascript
Vue监听页面刷新和关闭功能
2019/06/20 Javascript
vue点击页面空白处实现保存功能
2019/11/06 Javascript
JS动态显示倒计时效果
2019/12/12 Javascript
跟老齐学Python之变量和参数
2014/10/10 Python
Python迭代器和生成器介绍
2015/03/06 Python
在Python中操作字典之fromkeys()方法的使用
2015/05/21 Python
Python使用ftplib实现简易FTP客户端的方法
2015/06/03 Python
python函数中return后的语句一定不会执行吗?
2017/07/06 Python
Python常用字符串替换函数strip、replace及sub用法示例
2018/05/21 Python
python用户评论标签匹配的解决方法
2018/05/31 Python
Selenium的使用详解
2018/10/19 Python
pandas对dataFrame中某一个列的数据进行处理的方法
2019/07/08 Python
HTML5页面中尝试调起APP功能
2017/09/12 HTML / CSS
I.T集团香港官方商城:ITeSHOP.com Hong Kong
2019/02/15 全球购物
解释一下ArrayList Vector和LinkedList的实现和区别
2013/04/26 面试题
体育教育个人自荐信范文
2013/12/01 职场文书
离职告别感言
2015/08/04 职场文书
《伯牙绝弦》教学反思
2016/02/16 职场文书