探寻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中使用PIPE操作Linux管道
Feb 04 Python
python判断字符串编码的简单实现方法(使用chardet)
Jul 01 Python
用Python实现KNN分类算法
Dec 22 Python
用TensorFlow实现lasso回归和岭回归算法的示例
May 02 Python
使用Django启动命令行及执行脚本的方法
May 29 Python
python得到电脑的开机时间方法
Oct 15 Python
django2.2安装错误最全的解决方案(小结)
Sep 24 Python
python实现KNN分类算法
Oct 16 Python
pycharm 2019 最新激活方式(pycharm破解、激活)
Sep 22 Python
python Paramiko使用示例
Sep 21 Python
如何利用Python实现一个论文降重工具
Jul 09 Python
python神经网络 使用Keras构建RNN训练
May 04 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防CC攻击实现代码
2011/12/29 PHP
php启动时候提示PHP startup的解决方法
2013/05/07 PHP
apache中为php 设置虚拟目录
2014/12/17 PHP
PHP使用内置dir类实现目录遍历删除
2015/03/31 PHP
php实现paypal 授权登录
2015/05/28 PHP
[原创]php token使用与验证示例【测试可用】
2017/08/30 PHP
PHP fclose函数用法总结
2019/02/15 PHP
setInterval 和 setTimeout会产生内存溢出
2008/02/15 Javascript
javascript 解析后的xml对象的读取方法细解
2009/07/25 Javascript
jQuery1.5.1 animate方法源码阅读
2011/04/05 Javascript
js改变img标签的src属性在IE下没反应的解决方法
2013/07/23 Javascript
常规表格多表头查询示例
2014/02/21 Javascript
Bootstrap每天必学之表单
2015/11/23 Javascript
JavaScript对象封装的简单实现方法(3种方法)
2017/01/03 Javascript
Javascript实现异步编程的过程
2018/06/18 Javascript
Vue封装的组件全局注册并引用
2019/07/24 Javascript
Vue实现图片轮播组件思路及实例解析
2020/05/11 Javascript
JS sort方法基于数组对象属性值排序
2020/07/10 Javascript
js实现微信聊天效果
2020/08/09 Javascript
Python求两个list的差集、交集与并集的方法
2014/11/01 Python
Python中Random和Math模块学习笔记
2015/05/18 Python
Python线程下使用锁的技巧分享
2018/09/13 Python
Python中xml和dict格式转换的示例代码
2019/11/07 Python
解决Python import docx出错DLL load failed的问题
2020/02/13 Python
PageFactory设计模式基于python实现
2020/04/14 Python
Python基于httpx模块实现发送请求
2020/07/07 Python
Python库安装速度过慢解决方案
2020/07/14 Python
Python sqlalchemy时间戳及密码管理实现代码详解
2020/08/01 Python
Python命令行参数定义及需要注意的地方
2020/11/30 Python
详解CSS3 rem(设置字体大小) 教程
2017/11/21 HTML / CSS
HTML5 Canvas画线技巧——实现绘制一个像素宽的细线
2013/08/02 HTML / CSS
沃达丰英国有限公司:Vodafone英国
2019/04/16 全球购物
Ever New美国:澳大利亚领先的女装时尚品牌
2019/11/28 全球购物
售后服务经理岗位职责
2014/02/25 职场文书
学校体育节班级口号
2015/12/25 职场文书
党章学习心得体会2016
2016/01/14 职场文书