探寻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下的subprocess模块的入门指引
Apr 16 Python
Linux系统上Nginx+Python的web.py与Django框架环境
Dec 25 Python
python生成验证码图片代码分享
Jan 28 Python
Python处理PDF及生成多层PDF实例代码
Apr 24 Python
Python基于更相减损术实现求解最大公约数的方法
Apr 04 Python
Django中数据库的数据关系:一对一,一对多,多对多
Oct 21 Python
Pycharm 实现下一个文件引用另外一个文件的方法
Jan 17 Python
手把手教你pycharm专业版安装破解教程(linux版)
Sep 26 Python
python 轮询执行某函数的2种方式
May 03 Python
Python使用Pygame绘制时钟
Nov 29 Python
Python Pandas pandas.read_sql函数实例用法
Jun 21 Python
python中取整数的几种方法
Nov 07 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递归遍历指定文件夹内的文件实现方法
2016/11/15 PHP
php实现多站点共用session实现单点登录的方法详解
2019/09/18 PHP
Aster vs KG BO3 第一场2.19
2021/03/10 DOTA
JAVASCRIPT keycode总结
2009/02/04 Javascript
JS 页面自动加载函数(兼容多浏览器)
2009/05/18 Javascript
理解Javascript_02_理解undefined和null
2010/10/11 Javascript
jquery绑定原理 简单解析与实现代码分享
2011/09/06 Javascript
网页前端优化之滚动延时加载图片示例
2013/07/13 Javascript
js实现的点击div区域外隐藏div区域
2014/06/30 Javascript
jQuery实现仿QQ在线客服效果的滚动层代码
2015/10/15 Javascript
详解angularJs中自定义directive的数据交互
2017/01/13 Javascript
jQuery+Ajax实现用户名重名实时检测
2017/06/01 jQuery
详解vue项目优化之按需加载组件-使用webpack require.ensure
2017/06/13 Javascript
原生js实现获取form表单数据代码实例
2019/03/27 Javascript
Vue使用Proxy监听所有接口状态的方法实现
2019/06/07 Javascript
JavaScript 实现自己的安卓手机自动化工具脚本(推荐)
2020/05/13 Javascript
[43:43]完美世界DOTA2联赛PWL S2 FTD.C vs Rebirth 第一场 11.22
2020/11/24 DOTA
研究Python的ORM框架中的SQLAlchemy库的映射关系
2015/04/25 Python
Python3 模块、包调用&路径详解
2017/10/25 Python
python爬取各类文档方法归类汇总
2018/03/22 Python
解决python中 f.write写入中文出错的问题
2018/10/31 Python
使用Pyhton集合set()实现成果查漏的例子
2019/11/24 Python
Python基于yield遍历多个可迭代对象
2020/03/12 Python
python3 logging日志封装实例
2020/04/08 Python
TensorFlow实现批量归一化操作的示例
2020/04/22 Python
python3.8.1+selenium实现登录滑块验证功能
2020/05/22 Python
Windows下Sqlmap环境安装教程详解
2020/08/04 Python
python3跳出一个循环的实例操作
2020/08/18 Python
Pycharm Plugins加载失败问题解决方案
2020/11/28 Python
使用Html5 Stream开发实时监控系统
2020/06/02 HTML / CSS
管理科学大学生求职信
2013/11/13 职场文书
保荐人的岗位职责
2013/11/19 职场文书
优秀员工个人的自我评价
2013/11/29 职场文书
体育课课后反思
2014/04/24 职场文书
大学生实习证明范文(5篇)
2014/09/18 职场文书
vue选项卡切换的实现案例
2022/04/11 Vue.js