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实现类继承实例
Jul 04 Python
Python中的True,False条件判断实例分析
Jan 12 Python
python先序遍历二叉树问题
Nov 10 Python
Python实现列表删除重复元素的三种常用方法分析
Nov 24 Python
python tornado微信开发入门代码
Aug 24 Python
Python使用sax模块解析XML文件示例
Apr 04 Python
在Python函数中输入任意数量参数的实例
Jul 16 Python
Python一行代码解决矩阵旋转的问题
Nov 30 Python
Python: 传递列表副本方式
Dec 19 Python
python GUI库图形界面开发之PyQt5结合Qt Designer创建信号与槽的详细方法与实例
Mar 08 Python
Django框架安装及项目创建过程解析
Sep 14 Python
python绕过图片滑动验证码实现爬取PTA所有题目功能 附源码
Jan 06 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 has encountered an Access Violation
2007/01/15 PHP
php横向重复区域显示二法
2008/09/25 PHP
将php数组输出html表格的方法
2014/02/24 PHP
ThinkPHP框架实现session跨域问题的解决方法
2014/07/01 PHP
深入理解PHP变量的值类型和引用类型
2015/10/21 PHP
详谈PHP程序Laravel 5框架的优化技巧
2016/07/18 PHP
PHP入门教程之字符串处理技巧总结(转换,过滤,解析,查找,截取,替换等)
2016/09/11 PHP
动态表单验证的操作方法和TP框架里面的ajax表单验证
2017/07/19 PHP
PHP单例模式模拟Java Bean实现方法示例
2018/12/07 PHP
laravel实现简单用户权限的示例代码
2019/05/28 PHP
javascript 放大镜效果js组件 qsoft.PopBigImage.v0.35 加入了chrome支持
2009/04/07 Javascript
jQuery:节点(插入,复制,替换,删除)操作
2013/03/04 Javascript
js定时器的使用(实例讲解)
2014/01/06 Javascript
jQuery检测输入的字符串包含的中英文的数量
2015/04/17 Javascript
举例详解Python中smtplib模块处理电子邮件的使用
2015/06/24 Javascript
jquery.serialize() 函数语法及简单实例
2016/07/08 Javascript
JS中作用域和变量提升(hoisting)的深入理解
2016/10/31 Javascript
原生js编写基于面向对象的分页组件
2016/12/05 Javascript
详解关于vue-area-linkage走过的坑
2018/06/27 Javascript
微信小程序swiper实现滑动放大缩小效果
2018/11/15 Javascript
swiper4实现移动端导航切换
2020/10/16 Javascript
python设置windows桌面壁纸的实现代码
2013/01/28 Python
python制作小说爬虫实录
2017/08/14 Python
Django组件content-type使用方法详解
2019/07/19 Python
简单了解python gevent 协程使用及作用
2019/07/22 Python
在Django中实现添加user到group并查看
2019/11/18 Python
Tensorflow中的图(tf.Graph)和会话(tf.Session)的实现
2020/04/22 Python
HTML5 微格式和相关的属性名称
2010/02/10 HTML / CSS
html5的新玩法——语音搜索
2013/01/03 HTML / CSS
DHC中国官方购物网站:日本通信销售No.1化妆品
2016/08/20 全球购物
龟牌英国商店:Turtle Wax Brand Store UK
2019/07/02 全球购物
清洁工表扬信
2014/01/08 职场文书
村党支部换届选举方案
2014/05/02 职场文书
公司向个人借款协议书范本
2014/10/09 职场文书
Java基础-封装和继承
2021/07/02 Java/Android
详解解Django 多对多表关系的三种创建方式
2021/08/23 Python