Python线程与os.system()调用。当使用ctrl+c时,主线程不会退出

6

在阅读之前,请不要认为这是重复问题,关于多线程和键盘中断有很多问题,但我没有找到任何考虑到os.system的问题,而且它看起来很重要。

我有一个Python脚本,在工作线程中进行一些外部调用。如果我按下ctrl+c,我希望它退出。但似乎主线程忽略了它。

像这样:

from threading import Thread
import sys
import os

def run(i):
    while True:
        os.system("sleep 10")
        print i

def main():
    threads=[]
    try:
        for i in range(0, 3):
            threads.append(Thread(target=run, args=(i,)))
            threads[i].daemon=True
            threads[i].start()
        for i in range(0, 3):
            while True:
                threads[i].join(10)
                if not threads[i].isAlive():
                    break

    except(KeyboardInterrupt, SystemExit):
        sys.exit("Interrupted by ctrl+c\n")


if __name__ == '__main__': 
    main() 

令人惊讶的是,如果我将os.system("sleep 10")更改为time.sleep(10),它可以正常工作。


你是否考虑使用subprocess模块呢? - moooeeeep
不将其作为答案,因为这是一个丑陋的黑客技巧,但是对于任何通过Google搜索 os.system 导致您的脚本忽略 Ctrl+C 的人来说,您可以执行 assert 0 == os.system("sleep 10")。这样,如果进程以除0以外的任何内容退出,就会引发 AssertionError。但实际上,您最好使用 subprocess。 - Alex Forbes
2个回答

6

我不确定你使用的是什么操作系统和Shell。我用Mac OS X和Linux,使用zsh (bash/sh应该类似)。

当你按下Ctrl+C时,当前终端中前台运行的所有程序收到SIGINT信号。在你的情况下,它是你的主Python进程以及由os.system生成的所有进程。

被os.system生成的进程随后终止执行。通常情况下,当Python脚本接收到SIGINT时,它会引发KeyboardInterrupt异常,但由于os.system(),你的主进程忽略了SIGINT。Python os.system()调用了标准的C函数system(),这使得调用进程忽略了SIGINT (Linux手册/Mac OS X手册)。

因此,你的Python线程都没有收到SIGINT信号,只有子进程收到了。

当你移除os.system()调用时,你的Python进程停止忽略SIGINT,你将会得到KeyboardInterrupt

你可以用subprocess.call(["sleep", "10"])替换os.system("sleep 10")subprocess.call()不会让你的进程忽略SIGINT。


1

我在学习Python多线程时,遇到了无数次同样的问题。

在循环内添加sleep调用会阻塞主线程,这将使其仍然能够听到并遵守异常。你需要做的是利用Event类在子线程中设置一个事件,作为退出标志以中断执行。你可以在主线程中的KeyboardInterrupt异常中设置此标志,只需将该except子句放在主线程中即可。

我不确定Python特定的sleep和操作系统调用的sleep之间的不同行为是什么,但我提供的解决方法应该适用于您所期望的最终结果。只是猜测,操作系统调用的sleep可能以不同的方式阻塞解释器本身?

请记住,在大多数需要线程的情况下,主线程将继续执行某些操作,在这种情况下,你简单示例中的“休眠”将被隐含。

http://docs.python.org/2/library/threading.html#event-objects


谢谢回复!但实际上似乎除了主进程之外,所有进程都接收到了信号。我不明白为什么会发生这种情况。有什么想法吗? - Shamdor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接