`
yiminghe
  • 浏览: 1460425 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

python多线程ctrl-c退出问题

阅读更多

场景:

 

经常会遇到下述问题:很多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()一定要放在线程创建前,原因未知。。。。,否则立刻就结束

 

 

ps2:类似的轮询join timeout

 

和我的实现太相似,可惜是后来总结时才发现,浪费脑细胞

1
1
分享到:
评论
2 楼 skyist 2012-03-29  
直接Ctrl+break代替Ctrl+C就行了
1 楼 toontong 2010-09-28  
thread_.setDaemon(True)
就可以在主线程退出时,此线程也退出

相关推荐

    探寻python多线程ctrl+c退出问题解决方案

    经常会遇到下述问题:很多io busy的应用采取多线程的方式来解决,但这时候会发现python命令行不响应ctrl-c 了,而对应的java代码则没有问题: 复制代码 代码如下: public class Test {   public static void main...

    Python中用Ctrl+C终止多线程程序的问题解决

    /bin/env python # -*- coding: utf-8 -*- #filename: peartest.py import threading, signal is_exit = False def doStress(i, cc): global is_exit idx = i while not is_exit: if (idx < 10000000):...

    python子线程退出及线程退出控制的代码

    下面通过代码给大家介绍python子线程退出问题,具体内容如下所示: def thread_func(): while True: #do something ...跑起来是没有问题的,但是使用ctrl + c中断的时候出问题了,主线程退出了,

    python多线程下信号处理程序示例

    python多线程中要响应Ctrl+C的信号以杀死整个进程,需要: 1.把所有子线程设为Daemon; 2.使用isAlive()函数判断所有子线程是否完成,而不是在主线程中用join()函数等待完成; 3.写一个响应Ctrl+C信号的函数,修改...

    Python库 | Pyquitter-11.5.14.tar.gz

    Pyquitter可以监听系统信号(如SIGINT,即用户中断,通常由Ctrl+C触发),并在接收到这些信号时执行相应的退出操作。这使得开发者无需手动处理这些信号,提高了代码的可读性和维护性。 ### 3. 使用示例 下面是一个...

    对python捕获ctrl+c手工中断程序的两种方法详解

    在Python编程中,有时我们需要处理用户通过按下`Ctrl+C`手工中断程序的情况。这通常会导致程序突然终止,而没有机会清理资源或执行任何必要的退出操作。本文将详细讲解两种捕获`Ctrl+C`中断并优雅地停止Python程序的...

    Python之signal模块的使用.docx

    6. **`KeyboardInterrupt`异常**:在Python中,`KeyboardInterrupt`是用户中断(通常是Ctrl+C)的异常。在上面的脚本中,当接收到`SIGINT`时,异常被触发,导致程序终止。 7. **信号的异步性质**:信号可以在任何...

    2021-2022计算机二级等级考试试题及答案No.10444.docx

    - **多线程**:在一个程序中同时执行多个任务的能力。 **题目解析:** 题目询问了创建线程的两种正确方法。 **正确答案:** - AC。创建线程可以通过继承Thread类或实现Runnable接口来完成。 ### 16. VFP - 保存...

    python使用信号量动态更新配置文件的操作

    Python中的信号量是一种同步机制,常用于多线程编程中控制对共享资源的访问,防止多个线程同时访问导致的数据不一致。然而,在这个场景中,信号量被用来处理操作系统发送的信号,如SIGHUP(挂断信号),而不是作为...

    2021-2022计算机二级等级考试试题及答案No.12351.docx

    - **知识点**:Ctrl+A是常用的快捷键之一,用于快速选择整个文档或工作表的内容。 ### 21. JSP注释 - **知识点**:JSP页面中使用`<%-- 注释内容 --%>`来进行注释。 - 这种注释方式不会在客户端显示。 ### 22. ...

    Python实现线程池代码分享

    Python线程池是一种多线程处理形式,它允许程序员创建一组预先启动的线程,然后将任务添加到队列中,由这些线程去执行。这样可以提高程序的并发性能,减少线程创建和销毁的开销。在Python中,线程池的实现通常依赖于...

    2021-2022计算机二级等级考试试题及答案No.13016.docx

    - **使用Runnable接口**:实现多线程的一种方式是通过定义实现了`Runnable`接口的类,并在其中实现`run()`方法。之后创建线程对象并通过`start()`方法启动该线程。 ### 10. 数据库系统的特点 - **数据共享**:...

    2021-2022计算机二级等级考试试题及答案No.17865.docx

    - **详细说明**:Java支持多线程、具有良好的跨平台能力、动态性等特点。但是Java不支持多继承,它通过接口来实现多重继承的功能。 ### 18. PHP中的变量定义 - **知识点概述**:PHP中变量的定义规则。 - **详细...

    C#版挂机锁

    3. **线程同步**:如果程序涉及多线程,确保在挂机锁激活期间,关键操作的线程不会被其他线程中断。 4. **异常处理**:对可能出现的异常进行捕获和处理,避免因异常导致程序意外终止。 5. **界面锁定**:可禁用除...

    2021-2022计算机二级等级考试试题及答案No.1626.docx

    9. 给定的C语言do...while循环程序会打印23,因为x--先执行再判断条件,当x为0时退出循环,所以不输出任何内容的选项C错误,答案是B。 10. 1MB确实等于1024KB,这是存储容量的基本单位换算,选项正确。 11. 算法的...

    2021-2022计算机二级等级考试试题及答案No.11942.docx

    - `Ctrl+C` 用于复制选定的文本或对象。 - `Ctrl+O` 用于打开文件。 故正确答案为 A。 ### 21. 循环结构 **题目描述:** 执行如下程序片段的结果是什么? **知识点解析:** 根据提供的代码片段,缺少了循环的...

Global site tag (gtag.js) - Google Analytics