探寻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自动化工具日志查询分析脚本代码实现
Nov 26 Python
解决Python中字符串和数字拼接报错的方法
Oct 23 Python
Python网络爬虫出现乱码问题的解决方法
Jan 05 Python
Python实现多线程HTTP下载器示例
Feb 11 Python
Django基础之Model操作步骤(介绍)
May 27 Python
python中partial()基础用法说明
Dec 30 Python
基于python及pytorch中乘法的使用详解
Dec 27 Python
python numpy数组中的复制知识解析
Feb 03 Python
Python列表解析操作实例总结
Feb 26 Python
python使用信号量动态更新配置文件的操作
Apr 01 Python
python正则表达式的懒惰匹配和贪婪匹配说明
Jul 13 Python
Python实现Canny及Hough算法代码实例解析
Aug 06 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脚本的10个技巧(8)
2006/10/09 PHP
PHP采集利器 Snoopy 试用心得
2011/07/03 PHP
php基于session实现数据库交互的类实例
2015/08/03 PHP
Yii控制器中操作视图js的方法
2016/07/04 PHP
php与c 实现按行读取文件实例代码
2017/01/03 PHP
Laravel 手动开关 Eloquent 修改器的操作方法
2019/12/30 PHP
写自已的js类库需要的核心代码
2012/07/16 Javascript
jquery中的过滤操作详细解析
2013/12/02 Javascript
javascript搜索框点击文字消失失焦时文本出现
2014/09/18 Javascript
让javascript加载速度倍增的方法(解决JS加载速度慢的问题)
2014/12/12 Javascript
js读取并解析JSON类型数据的方法
2015/11/14 Javascript
Vue通过URL传参如何控制全局console.log的开关详解
2017/12/07 Javascript
Angular4实现图片上传预览路径不安全的问题解决
2017/12/25 Javascript
JavaScript类数组对象转换为数组对象的方法实例分析
2018/07/24 Javascript
微信小程序首页的分类功能和搜索功能的实现思路及代码详解
2018/09/11 Javascript
详解js常用分割取字符串的方法
2019/05/15 Javascript
微信小程序点餐系统开发常见问题汇总
2019/08/06 Javascript
Javascript 关于基本类型和引用类型的个人理解
2019/11/01 Javascript
vue-router 2.0 跳转之router.push()用法说明
2020/08/12 Javascript
Nuxt的路由动画效果案例
2020/11/06 Javascript
Vue实现todo应用的示例
2021/02/20 Vue.js
[06:10]6.81新信使新套装!给你一个炫酷的DOTA2
2014/05/06 DOTA
[00:03]DOTA2新版本PA至宝展示
2014/11/19 DOTA
python检测远程udp端口是否打开的方法
2015/03/14 Python
简单介绍Python的Django框架加载模版的方式
2015/07/20 Python
python爬虫---requests库的用法详解
2020/09/28 Python
详解pandas赋值失败问题解决
2020/11/29 Python
python实现三种随机请求头方式
2021/01/05 Python
巴西网上药房:onofre
2016/11/21 全球购物
冰淇淋店的创业计划书
2014/02/07 职场文书
《黄山奇石》教学反思
2014/04/19 职场文书
5s标语大全
2014/06/23 职场文书
法制教育演讲稿
2014/09/10 职场文书
工人先进事迹材料
2014/12/26 职场文书
小学生安全教育心得体会
2016/01/15 职场文书
MYSQL主从数据库同步备份配置的方法
2021/05/26 MySQL