探寻python多线程ctrl+c退出问题解决方案


Posted in Python onOctober 23, 2014

场景:

经常会遇到下述问题:很多io busy的应用采取多线程的方式来解决,但这时候会发现python命令行不响应ctrl-c 了,而对应的java代码则没有问题:

public class Test {  

    public static void main(String[] args) throws Exception {  

  

        new Thread(new Runnable() {  

  

            public void run() {  

                long start = System.currentTimeMillis();  

                while (true) {  

                    try {  

                        Thread.sleep(1000);  

                    } catch (Exception e) {  

                    }  

                    System.out.println(System.currentTimeMillis());  

                    if (System.currentTimeMillis() - start > 1000 * 100) break;  

                }  

            }  

        }).start();  

  

    }  

}  

java Test

ctrl-c则会结束程序

而对应的python代码:

# -*- coding: utf-8 -*-  

import time  

import threading  

start=time.time()  

def foreverLoop():  

    start=time.time()  

    while 1:  

        time.sleep(1)  

        print time.time()  

        if time.time()-start>100:  

            break  

               

thread_=threading.Thread(target=foreverLoop)  

#thread_.setDaemon(True)  

thread_.start() 

python p.py

后ctrl-c则完全不起作用了。

不成熟的分析:

首先单单设置 daemon 为 true 肯定不行,就不解释了。当daemon为 false 时,导入python线程库后实际上,threading会在主线程执行完毕后,检查是否有不是 daemon 的线程,有的化就wait,等待线程结束了,在主线程等待期间,所有发送到主线程的信号也会被阻测,可以在上述代码加入signal模块验证一下:

def sigint_handler(signum,frame):    

    print "main-thread exit"  

    sys.exit()    

signal.signal(signal.SIGINT,sigint_handler) 

在100秒内按下ctrl-c没有反应,只有当子线程结束后才会出现打印 "main-thread exit",可见 ctrl-c被阻测了

threading 中在主线程结束时进行的操作:

_shutdown = _MainThread()._exitfunc  

def _exitfunc(self):  

        self._Thread__stop()  

        t = _pickSomeNonDaemonThread()  

        if t:  

            if __debug__:  

                self._note("%s: waiting for other threads", self)  

        while t:  

            t.join()  

            t = _pickSomeNonDaemonThread()  

        if __debug__:  

            self._note("%s: exiting", self)  

        self._Thread__delete()  

 

 对所有的非daemon线程进行join等待,其中join中可自行察看源码,又调用了wait,同上文分析 ,主线程等待到了一把锁上。

不成熟的解决:

只能把线程设成daemon才能让主线程不等待,能够接受ctrl-c信号,但是又不能让子线程立即结束,那么只能采用传统的轮询方法了,采用sleep间歇省点cpu吧:
 

# -*- coding: utf-8 -*-  

import time,signal,traceback  

import sys  

import threading  

start=time.time()  

def foreverLoop():  

    start=time.time()  

    while 1:  

        time.sleep(1)  

        print time.time()  

        if time.time()-start>5:  

            break  

              

thread_=threading.Thread(target=foreverLoop)  

thread_.setDaemon(True)  

thread_.start()  

  

#主线程wait住了,不能接受信号了  

#thread_.join()  

  

def _exitCheckfunc():  

    print "ok"  

    try:  

        while 1:  

            alive=False  

            if thread_.isAlive():  

                alive=True  

            if not alive:  

                break  

            time.sleep(1)    

    #为了使得统计时间能够运行,要捕捉  KeyboardInterrupt :ctrl-c        

    except KeyboardInterrupt, e:  

        traceback.print_exc()  

    print "consume time :",time.time()-start  

          

threading._shutdown=_exitCheckfunc 

   缺点:轮询总会浪费点cpu资源,以及battery.

有更好的解决方案敬请提出。

ps1: 进程监控解决方案 :

用另外一个进程来接受信号后杀掉执行任务进程,牛

# -*- coding: utf-8 -*-  

import time,signal,traceback,os  

import sys  

import threading  

start=time.time()  

def foreverLoop():  

    start=time.time()  

    while 1:  

        time.sleep(1)  

        print time.time()  

        if time.time()-start>5:  

            break  

  

class Watcher:  

    """this class solves two problems with multithreaded 

    programs in Python, (1) a signal might be delivered 

    to any thread (which is just a malfeature) and (2) if 

    the thread that gets the signal is waiting, the signal 

    is ignored (which is a bug). 

 

    The watcher is a concurrent process (not thread) that 

    waits for a signal and the process that contains the 

    threads.  See Appendix A of The Little Book of Semaphores. 

    http://greenteapress.com/semaphores/ 

 

    I have only tested this on Linux.  I would expect it to 

    work on the Macintosh and not work on Windows. 

    """  

  

    def __init__(self):  

        """ Creates a child thread, which returns.  The parent 

            thread waits for a KeyboardInterrupt and then kills 

            the child thread. 

        """  

        self.child = os.fork()  

        if self.child == 0:  

            return  

        else:  

            self.watch()  

  

    def watch(self):  

        try:  

            os.wait()  

        except KeyboardInterrupt:  

            # I put the capital B in KeyBoardInterrupt so I can  

            # tell when the Watcher gets the SIGINT  

            print 'KeyBoardInterrupt'  

            self.kill()  

        sys.exit()  

  

    def kill(self):  

        try:  

            os.kill(self.child, signal.SIGKILL)  

        except OSError: pass  

  

Watcher()              

thread_=threading.Thread(target=foreverLoop)  

thread_.start() 

 注意 watch()一定要放在线程创建前,原因未知。。。。,否则立刻就结束

Python 相关文章推荐
Python挑选文件夹里宽大于300图片的方法
Mar 05 Python
在python的类中动态添加属性与生成对象
Sep 17 Python
python 异常处理总结
Oct 18 Python
python利用matplotlib库绘制饼图的方法示例
Dec 18 Python
Python科学画图代码分享
Nov 29 Python
Python实现可自定义大小的截屏功能
Jan 20 Python
快速解决PyCharm无法引用matplotlib的问题
May 24 Python
Python中类的创建和实例化操作示例
Feb 27 Python
python 多进程共享全局变量之Manager()详解
Aug 15 Python
Python处理session的方法整理
Aug 29 Python
python 实现仿微信聊天时间格式化显示的代码
Apr 17 Python
python中 _、__、__xx__()区别及使用场景
Jun 30 Python
纯Python开发的nosql数据库CodernityDB介绍和使用实例
Oct 23 #Python
Python中使用scapy模拟数据包实现arp攻击、dns放大攻击例子
Oct 23 #Python
使用Python开发windows GUI程序入门实例
Oct 23 #Python
手动实现把python项目发布为exe可执行程序过程分享
Oct 23 #Python
python文件操作整理汇总
Oct 21 #Python
Python中input和raw_input的一点区别
Oct 21 #Python
Python中if __name__ == "__main__"详细解释
Oct 21 #Python
You might like
详解PHP导入导出CSV文件
2014/11/03 PHP
隐性调用php程序的方法
2015/06/13 PHP
PHP7+Nginx的配置与安装教程详解
2016/05/10 PHP
PHP编写的图片验证码类文件分享
2016/06/06 PHP
JavaScript取得鼠标绝对位置程序代码介绍
2012/09/16 Javascript
javascript使用onclick事件改变选中行的颜色
2013/12/30 Javascript
影响jQuery使用的14个方面
2014/09/01 Javascript
jquery实现简单的二级导航下拉菜单效果
2015/09/07 Javascript
javascript实现tab切换的两个实例
2015/11/05 Javascript
Bootstrap实现响应式导航栏效果
2015/12/28 Javascript
nodeJs爬虫获取数据简单实现代码
2016/03/29 NodeJs
使用RequireJS库加载JavaScript模块的实例教程
2016/06/06 Javascript
基于jQuery实现淡入淡出效果轮播图
2020/07/31 Javascript
JS对象深度克隆实例分析
2017/03/16 Javascript
JS实现提交表单前的数字及邮箱校检功能
2017/11/13 Javascript
Angular5.1新功能分享
2017/12/21 Javascript
Vue render深入开发讲解
2018/04/13 Javascript
讲解vue-router之什么是动态路由
2018/05/28 Javascript
JS实现图片上传多次上传同一张不生效的处理方法
2018/08/06 Javascript
vue单页缓存方案分析及实现
2018/09/25 Javascript
AngularJS实现的自定义过滤器简单示例
2019/02/02 Javascript
JS/jQuery实现超简单的Table表格添加,删除行功能示例
2019/07/31 jQuery
swiper实现异形轮播效果
2019/11/28 Javascript
Node Mongoose用法详解【Mongoose使用、Schema、对象、model文档等】
2020/05/13 Javascript
[01:32]2016国际邀请赛中国区预选赛CDEC战队教练采访
2016/06/26 DOTA
[01:10:24]DOTA2-DPC中国联赛 正赛 VG vs Aster BO3 第一场 2月28日
2021/03/11 DOTA
pygame学习笔记(2):画点的三种方法和动画实例
2015/04/15 Python
Django contenttypes 框架详解(小结)
2018/08/13 Python
详解Python匿名函数(lambda函数)
2019/04/19 Python
python 用pandas实现数据透视表功能
2020/12/21 Python
教你一分钟在win10终端成功安装Pytorch的方法步骤
2021/01/28 Python
利用异或运算实现两个无符号数的加法运算
2013/12/20 面试题
幼儿园五一活动方案
2014/02/07 职场文书
《乌鸦和狐狸》教学反思
2014/02/08 职场文书
学习朴航瑛老师爱岗敬业先进事迹思想汇报
2014/09/17 职场文书
2015年园林绿化工作总结
2015/05/23 职场文书