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进阶教程之循环相关函数range、enumerate、zip
Aug 30 Python
python下paramiko模块实现ssh连接登录Linux服务器
Jun 03 Python
python3.6连接MySQL和表的创建与删除实例代码
Dec 28 Python
Python进阶之尾递归的用法实例
Jan 31 Python
基于Python socket的端口扫描程序实例代码
Feb 09 Python
python K近邻算法的kd树实现
Sep 06 Python
Python 从相对路径下import的方法
Dec 04 Python
python random从集合中随机选择元素的方法
Jan 23 Python
Python3 Click模块的使用方法详解
Feb 12 Python
python实现PCA降维的示例详解
Feb 24 Python
django ListView的使用 ListView中获取url中的参数值方式
Mar 27 Python
python用700行代码实现http客户端
Jan 14 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
《Re:从零开始的异世界生活 冰结之绊》
2020/04/09 日漫
简单介绍下 PHP5 中引入的 MYSQLI的用途
2007/03/19 PHP
php empty函数判断mysql表单是否为空
2010/04/12 PHP
总结PHP代码规范、流程规范、git规范
2018/06/18 PHP
php 实现简单的登录功能示例【基于thinkPHP框架】
2019/12/02 PHP
js+xml生成级联下拉框代码
2012/07/24 Javascript
简介JavaScript中valueOf()方法的使用
2015/06/05 Javascript
javascript实现图片延迟加载方法汇总(三种方法)
2015/08/27 Javascript
JavaScript 封装一个tab效果源码分享
2015/09/15 Javascript
AngularJS中处理多个promise的方式
2016/02/02 Javascript
jQuery实现可以编辑的表格实例详解【附demo源码下载】
2016/07/09 Javascript
JS复制对应id的内容到粘贴板(Ctrl+C效果)
2017/01/23 Javascript
完美解决input[type=number]无法显示非数字字符的问题
2017/02/28 Javascript
nodejs基于mssql模块连接sqlserver数据库的简单封装操作示例
2018/01/05 NodeJs
在vue中使用Autoprefixed的方法
2018/07/27 Javascript
关于Vue Router中路由守卫的应用及在全局导航守卫中检查元字段的方法
2018/12/09 Javascript
详解vuex commit保存数据技巧
2018/12/25 Javascript
JavaScript中工厂函数与构造函数示例详解
2019/05/06 Javascript
python 3.6 tkinter+urllib+json实现火车车次信息查询功能
2017/12/20 Python
基于Python中单例模式的几种实现方式及优化详解
2018/01/09 Python
PyQt5实现类似别踩白块游戏
2019/01/24 Python
详解django+django-celery+celery的整合实战
2019/03/19 Python
Python pip替换为阿里源的方法步骤
2019/07/02 Python
Pyinstaller 打包exe教程及问题解决
2019/08/16 Python
详解mac python+selenium+Chrome 简单案例
2019/11/08 Python
tensorflow 保存模型和取出中间权重例子
2020/01/24 Python
tensorflow pb to tflite 精度下降详解
2020/05/25 Python
Selenium alert 弹窗处理的示例代码
2020/08/06 Python
如何使用python自带IDLE的几种方法
2020/10/10 Python
让IE下支持Html5的placeholder属性的插件
2014/09/02 HTML / CSS
李维斯牛仔裤荷兰官方网站:Levi’s NL
2020/08/23 全球购物
事业单位竞聘上岗实施方案
2014/03/28 职场文书
尊老爱幼演讲稿
2014/09/04 职场文书
2014年实验室工作总结
2014/12/03 职场文书
闪闪红星观后感
2015/06/08 职场文书
WebWorker 封装 JavaScript 沙箱详情
2021/11/02 Javascript