探寻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中使用百度音乐搜索的api下载指定歌曲的lrc歌词
Jul 18 Python
在GitHub Pages上使用Pelican搭建博客的教程
Apr 25 Python
Python Django使用forms来实现评论功能
Aug 17 Python
python Selenium爬取内容并存储至MySQL数据库的实现代码
Mar 16 Python
go和python变量赋值遇到的一个问题
Aug 31 Python
Python爬虫实战:分析《战狼2》豆瓣影评
Mar 26 Python
python socket网络编程之粘包问题详解
Apr 28 Python
Python之用户输入的实例
Jun 22 Python
简单了解python中的f.b.u.r函数
Nov 02 Python
解决Python3下map函数的显示问题
Dec 04 Python
python GUI库图形界面开发之PyQt5时间控件QTimer详细使用方法与实例
Feb 26 Python
python redis存入字典序列化存储教程
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
在CentOS上搭建LAMP+vsftpd环境的简单指南
2015/08/01 PHP
PHP自定义多进制的方法
2016/11/03 PHP
利用php生成验证码
2017/02/23 PHP
xmlHTTP实例
2006/10/24 Javascript
document.body.scrollTop 值总为0的解决方法 比较常见的标准问题
2009/11/30 Javascript
javascript 匿名函数的理解(透彻版)
2010/01/28 Javascript
jquery 插件学习(一)
2012/08/06 Javascript
jQuery获取对象简单实现方法小结
2014/10/30 Javascript
js性能优化技巧
2015/11/29 Javascript
Vue.js 60分钟快速入门教程
2017/03/28 Javascript
基于代数方程库Algebra.js解二元一次方程功能示例
2017/06/09 Javascript
基于Swiper实现移动端页面图片轮播效果
2017/12/28 Javascript
详解js的作用域、预解析机制
2018/02/05 Javascript
jquery UI实现autocomplete在获取焦点时得到显示列表功能示例
2019/06/04 jQuery
Handtrack.js库实现实时监测手部运动(推荐)
2021/02/08 Javascript
[02:44]完美大师赛主赛事淘汰赛第二日观众采访
2017/11/24 DOTA
用实例说明python的*args和**kwargs用法
2013/11/01 Python
python动态加载包的方法小结
2016/04/18 Python
详解Python中for循环是如何工作的
2017/06/30 Python
Python查找两个有序列表中位数的方法【基于归并算法】
2018/04/20 Python
python基础梳理(一)(推荐)
2019/04/06 Python
详解解决Python memory error的问题(四种解决方案)
2019/08/08 Python
python栈的基本定义与使用方法示例【初始化、赋值、入栈、出栈等】
2019/10/24 Python
python实现串口通信的示例代码
2020/02/10 Python
python由已知数组快速生成新数组的方法
2020/04/08 Python
PyCharm设置Ipython交互环境和宏快捷键进行数据分析图文详解
2020/04/23 Python
keras 自定义loss model.add_loss的使用详解
2020/06/22 Python
苏格兰销售女装、男装和童装的连锁店:M&Co
2018/03/16 全球购物
Surfdome西班牙:世界上最受欢迎的生活方式品牌
2019/02/13 全球购物
Roxy俄罗斯官方网站:冲浪和滑雪板的一切
2020/06/20 全球购物
模具专业推荐信
2013/10/30 职场文书
党支部四风整改方案
2014/10/25 职场文书
财务工作失误检讨书
2015/02/19 职场文书
给老婆的保证书怎么写
2015/05/08 职场文书
结婚喜宴迎宾词
2015/08/10 职场文书
分享mysql的current_timestamp小坑及解决
2021/11/27 MySQL