为什么我的线程/多处理python脚本没有正确退出?

时间:2012-02-04 07:14:51

标签: python multithreading multiprocessing sigint keyboardinterrupt

我有一个服务器脚本,我需要能够干净地关闭。在测试通常的try..except语句时,我意识到Ctrl-C没有按常规方式工作。通常我会像这样包装长时间运行的任务

try:
    ...
except KeyboardInterrupt:
    #close the script cleanly here

因此可以在Ctrl-C上干净地关闭任务。我之前从未遇到任何问题,但是当我在运行此特定脚本时点击Ctrl-C时,脚本只是退出而没有捕获Ctrl-C

初始版本是使用Process中的multiprocessing实施的。我使用Thread中的threading重新编写了脚本,但问题相同。我以前曾多次使用threading,但我是multiprocessing库的新手。无论哪种方式,我之前从未经历过这种Ctrl-C行为。

通常情况下,我一直在实施哨兵等以有序的方式关闭QueuesThread个实例,但这个脚本只是退出而没有任何回复。

最后,我尝试覆盖signal.SIGINT以及此类

def handler(signal, frame):
    print 'Ctrl+C'

signal.signal(signal.SIGINT, handler)
...

这里Ctrl+C实际被捕获,但处理程序没有执行,它从不打印任何东西。

threading / multiprocessing方面外,部分脚本还包含C++ SWIG个对象。我不知道这与它有什么关系。我在OS X Lion上运行Python 2.7.2。

所以,有几个问题:

  1. 这里发生了什么事?
  2. 我该如何调试?
  3. 为了解根本原因,我需要学习什么?
  4. 请注意:脚本的内部是专有的,所以我不能给出代码示例。然而,我非常愿意接收指针,所以我可以自己调试。如果有人能指出我正确的方向,我有足够的经验能够弄明白。

    编辑:我开始评论导入等,以查看导致这种奇怪行为的原因,并将其缩小为导入C++ SWIG库。有关导入C++ SWIG图书馆'窃取'Ctrl-C的任何想法?我不是有罪图书馆的作者,但我的SWIG经验有限,所以不知道从哪里开始......

    编辑2:我刚在Windows机器上尝试了相同的脚本,而在Windows 7中Ctrl-C按预期捕获。我真的不打算使用OS X部分,无论如何,脚本都将在Windows环境中运行。

3 个答案:

答案 0 :(得分:4)

这可能与Python管理线程,信号和C调用的方式有关。

简而言之 - Ctrl-C无法中断C调用,因为实现需要python线程处理信号,而不仅仅是任何线程,而是主线程(经常被阻塞,等待其他线程)。

事实上,长时间的操作可以阻止一切。

考虑一下:

>>> nums = xrange(100000000)
>>> -1 in nums
False (after  ~ 6.6 seconds)
>>>

现在,尝试按Ctrl-C(不间断!)

>>> nums = xrange(100000000)
>>> -1 in nums
^C^C^C   (nothing happens, long pause)
...
KeyboardInterrupt
>>>

Ctrl-C不适用于线程程序的原因是主线程经常被阻塞在不间断的线程连接或锁定上(例如,任何“等待”,“加入”或只是一个普通的空'main'线程,它在后台导致python在任何生成的线程上“加入”。)

尝试插入一个简单的

while True:
    time.sleep(1)

在主线程中。

如果您有一个长时间运行的C功能,请在C级进行信号处理(力量可能与您同在!)。

这很大程度上是基于David Beazley关于这个主题的video

答案 1 :(得分:1)

它退出是因为其他东西可能会捕获KeyboardInterupt然后引发其他异常,或者只是返回None。您仍然应该获得回溯以帮助调试。您需要捕获stderr输出或使用-i命令行选项运行脚本,以便查看回溯。另外,添加另一个除了块以捕获所有其他异常。

如果您怀疑C ++函数调用正在捕获CTRL + C,请尝试捕获它的输出。如果C函数没有返回任何内容,那么除了要求作者添加一些异常处理,返回代码等之外,你无能为力。

try:
    #Doing something proprietary  ...
    #catch the function call output
    result = yourCFuncCall()

    #raise an exception if it's not what you expected
    if result is None:
        raise ValueError('Unexpected Result')

except KeyboardInterupt:
    print('Must be a CTRL+C')
    return

except:
    print('Unhandled Exception')
    raise

答案 2 :(得分:0)