探寻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入门篇之字符串
Oct 17 Python
尝试使用Python多线程抓取代理服务器IP地址的示例
Nov 09 Python
Python 出现错误TypeError: ‘NoneType’ object is not iterable解决办法
Jan 12 Python
python实现单线程多任务非阻塞TCP服务端
Jun 13 Python
Python处理Excel文件实例代码
Jun 20 Python
利用Pandas读取文件路径或文件名称包含中文的csv文件方法
Jul 04 Python
python的pip安装以及使用教程
Sep 18 Python
python Django里CSRF 对应策略详解
Aug 05 Python
在Django下测试与调试REST API的方法详解
Aug 29 Python
Python ADF 单位根检验 如何查看结果的实现
Jun 03 Python
浅谈sklearn中predict与predict_proba区别
Jun 28 Python
pytorch加载语音类自定义数据集的方法教程
Nov 10 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 和 MySQL 开发的 8 个技巧
2007/01/02 PHP
php smarty模版引擎中的缓存应用
2009/12/11 PHP
php分页代码学习示例分享
2014/02/20 PHP
详解PHP的Yii框架中自带的前端资源包的使用
2016/03/31 PHP
PHP基于ip2long实现IP转换整形
2020/12/11 PHP
$.format,jquery.format 使用说明
2011/07/13 Javascript
js调用浏览器打印模块实现点击按钮触发自定义函数
2014/03/21 Javascript
JavaScript实现关键字高亮功能
2014/11/12 Javascript
JavaScript返回0-1之间随机数的方法
2015/04/06 Javascript
简单介绍JavaScript的变量和数据类型
2015/06/03 Javascript
JavaScript实现非常简单实用的下拉菜单效果
2015/08/27 Javascript
mvvm双向绑定机制的原理和实现代码(推荐)
2016/06/07 Javascript
Bootstrap弹出带合法性检查的登录框实例代码【推荐】
2016/06/23 Javascript
javascript弹出带文字信息的提示框效果
2016/07/19 Javascript
简单理解Vue条件渲染
2016/12/03 Javascript
js仿微博动态栏功能
2017/02/22 Javascript
详解ECMAScript6入门--Class对象
2017/04/27 Javascript
vue-router 权限控制的示例代码
2017/09/21 Javascript
es6新特性之 class 基本用法解析
2018/05/05 Javascript
微信小程序实现pdf、word等格式文件上传的方法
2019/09/10 Javascript
解决vue-router 二级导航默认选中某一选项的问题
2019/11/01 Javascript
Vue3新特性之在Composition API中使用CSS Modules
2020/07/13 Javascript
vant时间控件使用方法详解
2020/12/24 Javascript
[01:21]DOTA2周边文化主题展 神秘商店火热开售
2017/07/30 DOTA
[02:38]DOTA2 夜魇暗潮2020活动介绍官方视频
2020/11/04 DOTA
[45:34]完美世界DOTA2联赛PWL S3 Rebirth vs CPG 第一场 12.18
2020/12/19 DOTA
Python使用lxml模块和Requests模块抓取HTML页面的教程
2016/05/16 Python
Python 多线程Threading初学教程
2017/08/22 Python
hmac模块生成加入了密钥的消息摘要详解
2018/01/11 Python
一步步教你用python的scrapy编写一个爬虫
2019/04/17 Python
win10安装tesserocr配置 Python使用tesserocr识别字母数字验证码
2020/01/16 Python
基于FME使用Python过程图解
2020/05/13 Python
Sephora丝芙兰马来西亚官方网站:国际化妆品购物
2018/03/15 全球购物
荷兰最大的多品牌男装连锁店:Adam Brandstore
2019/12/31 全球购物
自荐信的五个重要部分
2013/10/29 职场文书
应届大专生自荐书
2014/06/16 职场文书