我想,当用户按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线程的数目后赶上的信号。
实际上有你的代码,让此行为两个不同的问题。 第一个是你的线程应该做成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)
请注意,使线程进入守护进程是不是在当前的例子绝对必要的,但我认为这是对于被认为主要程序结束时终止线程好的做法。
您可能需要阅读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