使用os.system()调用的Python线程。主线程不会在ctrl + c上退出

时间:2013-01-08 18:48:33

标签: python multithreading os.system keyboardinterrupt

在阅读之前请不要认为它是重复的。有很多关于multithreadingkeyboard interrupt的问题,但我没有找到任何考虑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),则可以正常工作。

2 个答案:

答案 0 :(得分:4)

我不确定您使用的是哪种操作系统和shell。我用zsh描述Mac OS X和Linux(bash / sh应该类似)。

当您按Ctrl + C时,所有程序都在当前终端receive the signal SIGINT的前台运行。在你的情况下,它是你的主要python进程和os.system产生的所有进程。

os.system产生的进程然后终止它们的执行。通常当python脚本收到SIGINT时,会引发KeyboardInterrupt异常,但由于os.system(),主进程会忽略SIGINT。 Python os.system() calls the Standard C function system(),使调用进程忽略SIGINT(man Linux / man Mac OS X)。

因此,你的python线程都没有收到SIGINT,只有子进程才能获得它。

当你删除os.system()时,你的python进程会停止忽略SIGINT,你得到KeyboardInterrupt

您可以将os.system("sleep 10")替换为subprocess.call(["sleep", "10"])subprocess.call()不会使您的进程忽略SIGINT。

答案 1 :(得分:1)

当我第一次学习python多线程时,我遇到的问题比我可以算回的次数多。

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

我不完全确定python特定睡眠和操作系统之间的不同行为是怎么回事,但我提供的补救措施应该适用于你想要的最终结果。只是提供一个猜测,一个叫做一个的操作系统可能会以不同的方式阻止解释器本身?

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

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