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进行数据提取的方法总结
Aug 22 Python
Python黑帽编程 3.4 跨越VLAN详解
Sep 28 Python
Python使用回溯法子集树模板获取最长公共子序列(LCS)的方法
Sep 08 Python
python cs架构实现简单文件传输
Mar 20 Python
Python 多线程不加锁分块读取文件的方法
Dec 11 Python
Python代码打开本地.mp4格式文件的方法
Jan 03 Python
python实现一行输入多个值和一行输出多个值的例子
Jul 16 Python
django数据关系一对多、多对多模型、自关联的建立
Jul 24 Python
python redis 批量设置过期key过程解析
Nov 26 Python
python3用urllib抓取贴吧邮箱和QQ实例
Mar 10 Python
Python语法垃圾回收机制原理解析
Mar 25 Python
使用Python拟合函数曲线
Apr 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
推荐个功能齐全的发送PHP邮件类
2007/01/03 PHP
执行、获取远程代码返回:file_get_contents 超时处理的问题详解
2013/06/25 PHP
用php简单实现加减乘除计算器
2014/01/06 PHP
destoon二次开发常用数据库操作
2014/06/21 PHP
php+ajax实现无刷新分页
2015/11/18 PHP
PHP进阶学习之垃圾回收机制详解
2019/06/18 PHP
在jQuery中 关于json空对象筛选替换
2013/04/15 Javascript
js中split函数的使用方法说明
2013/12/26 Javascript
js实现鼠标划过给div加透明度的方法
2015/05/25 Javascript
Spring mvc 接收json对象
2015/12/10 Javascript
JavaScript jquery及AJAX小结
2016/01/24 Javascript
VUEJS实战之构建基础并渲染出列表(1)
2016/06/13 Javascript
关于在Servelet中如何获取当前时间的操作方法
2016/06/28 Javascript
jQuery ajaxSubmit 实现ajax提交表单局部刷新
2016/07/04 Javascript
jquery插件uploadify多图上传功能实现代码
2016/08/12 Javascript
JavaScript简单下拉菜单特效
2016/09/13 Javascript
JS调用某段SQL语句的方法
2016/10/20 Javascript
Vue项目webpack打包部署到Tomcat刷新报404错误问题的解决方案
2018/05/15 Javascript
angularJs利用$scope处理升降序的方法
2018/10/08 Javascript
vuejs+element UI点击编辑表格某一行时获取内容填入表单的示例
2018/10/31 Javascript
Node.js爬虫如何获取天气和每日问候详解
2019/08/26 Javascript
微信小程序实现锚点功能
2019/11/20 Javascript
JS实现电商商品展示放大镜特效
2020/01/07 Javascript
Python编写带选项的命令行程序方法
2019/08/13 Python
Tensorflow 多线程设置方式
2020/02/06 Python
Django crontab定时任务模块操作方法解析
2020/09/10 Python
Carolina Lemke Berlin澳大利亚官网:时尚太阳镜品牌
2019/09/17 全球购物
体育教师自荐信范文
2013/12/16 职场文书
驾驶员岗位职责
2014/01/29 职场文书
网络技术专业求职信
2014/02/18 职场文书
企业办公室主任岗位职责
2014/02/19 职场文书
优秀经理获奖感言
2014/03/04 职场文书
2014年社区妇联工作总结
2014/12/02 职场文书
2015少先队大队辅导员工作总结
2015/07/24 职场文书
优秀创业计划书分享
2019/07/19 职场文书
SQL Server2019数据库备份与还原脚本,数据库可批量备份
2021/11/20 SQL Server