探寻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 15 Python
Python调用微信公众平台接口操作示例
Jul 08 Python
Python矩阵常见运算操作实例总结
Sep 29 Python
Python进阶之尾递归的用法实例
Jan 31 Python
python 二维数组90度旋转的方法
Jan 28 Python
python生成带有表格的图片实例
Feb 03 Python
python实现手机销售管理系统
Mar 19 Python
Pytorch 实现权重初始化
Dec 31 Python
python时间日期操作方法实例小结
Feb 06 Python
python爬虫容易学吗
Jun 02 Python
pytorch model.cuda()花费时间很长的解决
Jun 01 Python
Python多个MP4合成视频的实现方法
Jul 16 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 Memcached应用实现代码
2010/02/08 PHP
PHP字符串word末字符实现大小写互换的方法
2014/11/10 PHP
PHP实现简易图形计算器
2020/08/28 PHP
jQuery图片预加载 等比缩放实现代码
2011/10/04 Javascript
最流行的Node.js精简型和全栈型开发框架介绍
2015/02/26 Javascript
JavaScript去除数组里重复值的方法
2015/07/13 Javascript
jQuery获取checkbox选中的值
2016/01/28 Javascript
AngularJS基础 ng-paste 指令简单示例
2016/08/02 Javascript
AngularJs bootstrap搭载前台框架——准备工作
2016/09/01 Javascript
JavaScript队列、优先队列与循环队列
2016/11/14 Javascript
原生js编写焦点图效果
2016/12/08 Javascript
vue2.0 与 bootstrap datetimepicker的结合使用实例
2017/05/22 Javascript
vue-cli监听组件加载完成的方法
2018/09/07 Javascript
利用JS动态生成隔行换色HTML表格的两种方法
2018/10/09 Javascript
vue-cli3搭建项目的详细步骤
2018/12/05 Javascript
实例讲解JavaScript预编译流程
2019/01/24 Javascript
vue 表单之通过v-model绑定单选按钮radio
2019/05/13 Javascript
基于vue如何发布一个npm包的方法步骤
2019/05/15 Javascript
浅谈python字符串方法的简单使用
2016/07/18 Python
Python3 伪装浏览器的方法示例
2017/11/23 Python
Numpy中的mask的使用
2018/07/21 Python
PIL图像处理模块paste方法简单使用详解
2019/07/17 Python
python中常见错误及解决方法
2020/06/21 Python
Python装饰器结合递归原理解析
2020/07/02 Python
手把手教你如何用Pycharm2020.1.1配置远程连接的详细步骤
2020/08/07 Python
五分钟带你搞懂python 迭代器与生成器
2020/08/30 Python
荷兰和比利时时尚鞋店:Van Dalen
2018/04/23 全球购物
如何减少垃圾回收让内存更加有效使用
2013/10/18 面试题
大客户销售经理职责
2013/12/04 职场文书
最新奶茶店创业计划书范文
2014/02/08 职场文书
优秀少先队大队辅导员事迹材料
2014/05/04 职场文书
上市公司董事长岗位职责
2015/04/16 职场文书
2016幼儿园教师年度考核评语
2015/12/01 职场文书
2016年学校党支部公开承诺书
2016/03/25 职场文书
python中requests库+xpath+lxml简单使用
2021/04/29 Python
Node实现搜索框进行模糊查询
2021/06/28 Javascript