22

我想在 Python中用Ctrl+停止执行进程。C但我在某处读到KeyboardInterrupt异常仅在主线程中引发。我还读到在子线程执行时主线程被阻塞。那么如何杀死子线程呢?

例如,Ctrl+C对以下代码无效:

def main():
    try:
        thread = threading.Thread(target=f)
        thread.start()  # thread is totally blocking (e.g. while True)
        thread.join()
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        sys.exit(1)

def f():
    while True:
        pass  # do the actual work
4

3 回答 3

13

如果你想让主线程在加入时接收CTRL+C信号,可以通过添加超时join()调用来完成。

以下似乎有效(daemon=True如果您希望 main 实际结束,请不要忘记添加):

thread1.start()
while True:
    thread1.join(600)
    if not thread1.isAlive():
        break
于 2012-01-14T19:03:45.257 回答
10

问题在于您正在使用thread1.join(),这将导致您的程序等到该线程完成才能继续。

信号将始终被主进程捕获,因为它是接收信号的那个,它是具有线程的进程。

按照您的说明进行操作,您基本上是在运行一个没有线程功能的“普通”应用程序,因为您启动 1 个线程并等待它完成继续。

于 2011-01-20T16:11:54.167 回答
5

KeyboardInterrupt异常仅在每个进程的主线程中引发。但是该方法Thread.join阻塞了调用线程,包括 KeyboardInterrupt 异常。这就是为什么Ctrl+C似乎没有效果。

解决您的问题的一个简单方法是使方法Thread.join超时以解除阻塞KeyboardInterrupt异常,并使子线程守护进程以让父线程在退出时将其杀死(非守护子线程不会被杀死,而是在退出时由其父线程加入):

def main():
    try:
        thread = threading.Thread(target=f)
        thread.daemon = True  # let the parent kill the child thread at exit
        thread.start()
        while thread.is_alive():
            thread.join(1)  # time out not to block KeyboardInterrupt
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        sys.exit(1)

def f():
    while True:
        pass  # do the actual work

如果您控制子线程的代码,更好的解决方案是通知子线程优雅退出(而不是像简单的解决方案那样突然退出),例如使用threading.Event

def main():
    try:
        event = threading.Event()
        thread = threading.Thread(target=f, args=(event,))
        thread.start()
        event.wait()  # wait without blocking KeyboardInterrupt
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        event.set()  # notify the child thread to exit
        sys.exit(1)

def f(event):
    while not event.is_set():
        pass  # do the actual work
于 2019-02-19T13:36:17.500 回答