在阅读之前请不要认为它是重复的。有很多关于multithreading
和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)
,则可以正常工作。
答案 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