6

在阅读之前请不要认为它是重复的,有很多关于multithreadingand的问题keyboard 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).

4

2 回答 2

6

我不确定您使用的是什么操作系统和外壳。我用 zsh 来描述 Mac OS X 和 Linux(bash/sh 应该类似)。

当您按下 Ctrl+C 时,当前终端中在前台运行的所有程序都会收到信号 SIGINT。在您的情况下,它是您的主要 python 进程和 os.system 产生的所有进程。

由 os.system 产生的进程然后终止它们的执行。通常当 python 脚本接收到 SIGINT 时,它会引发 KeyboardInterrupt 异常,但是您的主进程会忽略 SIGINT,因为os.system(). Pythonos.system() 调用标准 C 函数 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。

于 2013-01-10T09:09:22.337 回答
1

当我第一次学习 python 多线程时,我遇到同样的问题的次数比我能数到的还要多。

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

我不完全确定 python 特定 sleep 和被称为 one 的操作系统之间的不同行为发生了什么,但我提供的补救措施应该适用于您想要的最终结果。只是提供一个猜测,被称为 one 的操作系统可能以不同的方式阻止解释器本身?

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

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

于 2013-01-09T18:50:19.673 回答