探寻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 分析Nginx访问日志并保存到MySQL数据库实例
Mar 13 Python
用实例分析Python中method的参数传递过程
Apr 02 Python
python字典get()方法用法分析
Apr 17 Python
利用Python爬取可用的代理IP
Aug 18 Python
利用Python实现Windows下的鼠标键盘模拟的实例代码
Jul 13 Python
Python设计模式之适配器模式原理与用法详解
Jan 15 Python
详解python函数的闭包问题(内部函数与外部函数详述)
May 17 Python
使用Python实现图像标记点的坐标输出功能
Aug 14 Python
Python任务调度模块APScheduler使用
Apr 15 Python
浅谈Python程序的错误:变量未定义
Jun 02 Python
Python 循环读取数据内存不足的解决方案
May 25 Python
Python一行代码实现自动发邮件功能
May 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函数
2006/10/09 PHP
PHP define函数的使用说明
2008/08/27 PHP
PHP中绘制图像的一些函数总结
2014/11/19 PHP
thinkphp模板输出技巧汇总
2014/11/24 PHP
TP3.2批量上传文件或图片 同名冲突问题的解决方法
2017/08/01 PHP
PHP使用HTML5 FileApi实现Ajax上传文件功能示例
2019/07/01 PHP
Laravel 前端资源配置教程
2019/10/18 PHP
php开发最强大的IDE编辑的phpstorm 2020.2配置Xdebug调试的详细教程
2020/08/17 PHP
javascript 播放器 控制
2007/01/22 Javascript
jQuery实现的立体文字渐变效果
2010/05/17 Javascript
常见的jQuery选择器汇总
2014/11/24 Javascript
angularJS中$apply()方法详解
2015/01/07 Javascript
jQuery实现字符串按指定长度加入特定内容的方法
2015/03/11 Javascript
nodejs实现获取某宝商品分类
2015/05/28 NodeJs
js实现当复选框选择匿名登录时隐藏登录框效果
2015/08/14 Javascript
jquery popupDialog 使用 加载jsp页面的方法
2016/10/25 Javascript
基于js中document.cookie全面解析
2017/09/14 Javascript
Vue下的国际化处理方法
2017/12/18 Javascript
vue中npm包全局安装和局部安装过程
2019/09/03 Javascript
基于vue-cli3创建libs库的实现方法
2019/12/04 Javascript
JavaScript正则表达式验证登录实例
2020/03/18 Javascript
node.js如何操作MySQL数据库
2020/10/29 Javascript
[02:41]DOTA2英雄基础教程 冥魂大帝
2014/01/16 DOTA
python中Flask框架简单入门实例
2015/03/21 Python
python实现将英文单词表示的数字转换成阿拉伯数字的方法
2015/07/02 Python
Python查找第n个子串的技巧分享
2018/06/27 Python
pytorch中tensor的合并与截取方法
2018/07/26 Python
把JSON数据格式转换为Python的类对象方法详解(两种方法)
2019/06/04 Python
用CSS3写的模仿iPhone中的返回按钮
2015/04/04 HTML / CSS
哥伦比亚最大的网上商店:Linio哥伦比亚
2016/09/25 全球购物
具有防紫外线功能的高性能钓鱼服装:Hook&Tackle
2018/08/16 全球购物
十佳青年个人事迹材料
2014/01/28 职场文书
2014年团工作总结
2014/11/27 职场文书
SpringBoot2 参数管理实践之入参出参与校验的方式
2021/06/16 Java/Android
Vue项目打包、合并及压缩优化网页响应速度
2021/07/07 Vue.js
Python使用BeautifulSoup4修改网页内容
2022/05/20 Python