探寻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匹配两个短语之间的字符实例
Dec 25 Python
Python实现的列表排序、反转操作示例
Mar 13 Python
python 并发编程 阻塞IO模型原理解析
Aug 20 Python
TensorFlow绘制loss/accuracy曲线的实例
Jan 21 Python
Python使用PyQt5/PySide2编写一个极简的音乐播放器功能
Feb 07 Python
Python基础之字符串常见操作经典实例详解
Feb 26 Python
解决django xadmin主题不显示和只显示bootstrap2的问题
Mar 30 Python
浅谈Python中threading join和setDaemon用法及区别说明
May 02 Python
使用python matploblib库绘制准确率,损失率折线图
Jun 16 Python
Python把图片转化为pdf代码实例
Jul 28 Python
plt.figure()参数使用详解及运行演示
Jan 08 Python
梳理总结Python开发中需要摒弃的18个坏习惯
Jan 22 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实现批量查询清除一句话后门的代码
2008/01/20 PHP
php 启动时报错的简单解决方法
2014/01/27 PHP
php表单请求获得数据求和示例
2014/05/15 PHP
ThinkPHP+EasyUI之ComboTree中的会计科目树形菜单实现方法
2017/06/09 PHP
laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
2019/11/14 PHP
php设计模式之建造器模式分析【星际争霸游戏案例】
2020/01/23 PHP
js 连接数据库如何操作数据库中的数据
2012/11/23 Javascript
jQuery点击tr实现checkbox选中的方法
2013/03/19 Javascript
node.js中的fs.futimes方法使用说明
2014/12/17 Javascript
轻松创建nodejs服务器(3):代码模块化
2014/12/18 NodeJs
javascript格式化指定日期对象的方法
2015/04/21 Javascript
javascript实现下班倒计时效果的方法(可桌面通知)
2015/07/10 Javascript
sso跨域写cookie的一段js脚本(推荐)
2016/05/25 Javascript
前端框架学习总结之Angular、React与Vue的比较详解
2017/03/14 Javascript
js实现文字列表无缝滚动效果
2017/06/23 Javascript
通过构造函数实例化对象的方法
2017/06/28 Javascript
Windows下快速搭建NodeJS本地服务器的步骤
2017/08/09 NodeJs
详解vue-meta如何让你更优雅的管理头部标签
2018/01/18 Javascript
详解node Async/Await 更好的异步编程解决方案
2018/05/10 Javascript
JavaScript继承定义与用法实践分析
2018/05/28 Javascript
vue在手机中通过本机IP地址访问webApp的方法
2018/08/15 Javascript
微信小程序webview 脚手架使用详解
2019/07/22 Javascript
koa2 数据api中间件设计模型的实现方法
2020/07/13 Javascript
原生js实现贪吃蛇游戏
2020/10/26 Javascript
详解Python中最难理解的点-装饰器
2017/04/03 Python
django文档学习之applications使用详解
2018/01/29 Python
对python:print打印时加u的含义详解
2018/12/15 Python
python利用Opencv实现人脸识别功能
2019/04/25 Python
Django 缓存配置Redis使用详解
2019/07/23 Python
Python写捕鱼达人的游戏实现
2020/03/31 Python
人事助理岗位职责
2013/11/18 职场文书
计算机专业毕业生的自我评价
2013/11/18 职场文书
2015年统计员个人工作总结
2015/07/23 职场文书
2019七夕节祝福语36句,快来收藏吧
2019/08/06 职场文书
Python编程super应用场景及示例解析
2021/10/05 Python
Redis 哨兵机制及配置实现
2022/03/25 Redis