-->

蟒蛇2.7:如何捕捉键盘中断程序中有超过25个线程(python 2.7: how to catch

2019-10-29 08:11发布

我想,当用户按Ctrl-C停止我的程序。 以下的答案表明捕捉KeyboardInterrupt异常。

蟒蛇:如何终止线程时主程序结束

有时,它的工作原理。 但在下面的例子中,它停止工作后,我增加线程的数量从25到30。

import threading, sys, signal, os

stderr_lock = threading.Lock()

def Log(module, msg):
    with stderr_lock:
        sys.stderr.write("%s: %s\n" % (module, msg))

class My_Thread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        Log("Init", "Initing.")
        self.start()
    def run(self):
        try:
            while True:
                Log("Run", "Running.")
        except KeyboardInterrupt:
            os._exit(0)

for i in range(30):
    My_Thread()

# trap ctrl-C in main thread
try:
    while True:
        pass
except KeyboardInterrupt:
    os._exit(0)

这有一个非常可疑的类似的感觉以下问题:

在Python 2.7线程安全的信号API

在这种情况下,我无法增大到超过87线程的数目后赶上的信号。

Answer 1:

实际上有你的代码,让此行为两个不同的问题。 第一个是你的线程应该做成daemon线程,让他们当主线程退出,第二个是你的自动停止try块不封装线程的创建和启动。

当您创建多个线程,线程的创建将无法完成相当长一段时间(因为它是不断被创建的线程中断, GIL防止它们并行运行)。 因此,你把你的KeyboardInterrupt被设置为处理之前。 然而, KeyboardInterrupt仍然会杀死主线程(用回溯),但不是子线程。

因此,你的代码的工作,如果你修改为:

import threading, sys, signal, os

stderr_lock = threading.Lock()

def Log(module, msg):
    with stderr_lock:
        sys.stderr.write("%s: %s\n" % (module, msg))

class My_Thread(threading.Thread):
    def __init__(self, value):
        threading.Thread.__init__(self)
        self.value = value
        Log("Init", "Initing %d." % self.value)
        self.daemon = True
        self.start()
    def run(self):
        while True:
            Log("Run", "Running %d." % self.value)

# trap ctrl-C in main thread
try:
    for i in range(1000):
        My_Thread(i)

    while True:
        pass
except KeyboardInterrupt:
    os._exit(0)

请注意,使线程进入守护进程是不是在当前的例子绝对必要的,但我认为这是对于被认为主要程序结束时终止线程好的做法。



Answer 2:

您可能需要阅读https://stackoverflow.com/a/35430500/1656850 ,即:

有3层出口的功能,除了提高SystemExit。

底层一个是os._exit,这需要1个int参数,并用无清理立即退出。 这是不可能的,你永远想摸摸这一个,但它的存在。

sys.exit在sysmodule.c定义和只是运行PyErr_SetObject(PyExc_SystemExit,exit_code);,其有效地是相同的直接提高SystemExit。 在精致的细节,提高SystemExit可能更快,因为sys.exit需要LOAD_ATTR和CALL_FUNCTION VS RAISE_VARARGS opcalls。 此外,提高SystemExit产生略小的字节码(4字节以内),(1个字节额外的,如果你从SYS进口出口使用,因为sys.exit有望复出无,因此包括一个额外POP_TOP)。

上次退出功能在site.py定义,并化名为退出或REPL退出。 它实际上是半途而废类(因此它可以有一个自定义再版的一个实例,因此可能是最慢的运行。此外,它关闭sys.stdin之前提高SystemExit,所以建议只在REPL使用。

至于SystemExit是如何处理的,它最终会导致VM调用os._exit,但在此之前,它会进行一些清理。 它也运行atexit._run_exitfuncs()它运行经由atexit对模块注册的任何回调。 调用os._exit直接绕过的atexit一步。

因此, raise SystemExit可能是异常被捕获时退出可取的方法。



文章来源: python 2.7: how to catch keyboard interrupt in program with more than 25 threads