扭曲与多处理事件和队列不兼容?

时间:2012-06-30 09:02:59

标签: python twisted multiprocessing

我正在尝试模拟使用twisted运行的应用程序网络。作为我的模拟的一部分,我想同步某些事件,并能够为每个进程提供大量数据。我决定使用多处理事件和队列。但是,我的进程被挂起了。

我编写了下面的示例代码来说明问题。具体来说,(在我的沙桥机器上大约95%的时间),'run_in_thread'函数完成,但是在我按下Ctrl-C之后才调用'print_done'回调。

此外,我可以更改示例代码中的一些内容,以使其更可靠地工作,例如:减少生成进程的数量,从reactor_ready调用self.ready.set或更改deferLater的延迟。

我猜在扭曲的反应堆和阻止多处理调用之间的某个地方存在竞争条件,例如Queue.get()或Event.wait()?

我遇到的问题究竟是什么?我的代码中是否有一个我遗漏的错误?我可以解决此问题,还是与多处理事件/队列不兼容?

其次,推荐替代spawnProcess或Ampoule吗? (如Mix Python Twisted with multiprocessing?中所述)

编辑(根据要求):

我遇到了所有反应堆的问题我曾尝试过glib2reactor selectreactor,pollreactor和epollreactor。 epollreactor似乎给出了最好的结果,并且似乎对于下面给出的示例工作正常,但仍然在我的应用程序中给出了相同(或类似)的问题。我会继续调查。

我正在运行Gentoo Linux内核3.3和3.4,python 2.7,我尝试过Twisted 10.2.0,11.0.0,11.1.0,12.0.0和12.1.0。

除了我的沙桥机器,我在双核amd机器上看到了同样的问题。

#!/usr/bin/python
# -*- coding: utf-8 *-*

from twisted.internet import reactor
from twisted.internet import threads
from twisted.internet import task

from multiprocessing import Process
from multiprocessing import Event

class TestA(Process):
    def __init__(self):
        super(TestA, self).__init__()
        self.ready = Event()
        self.ready.clear()
        self.start()

    def run(self):
        reactor.callWhenRunning(self.reactor_ready)
        reactor.run()

    def reactor_ready(self, *args):
        task.deferLater(reactor, 1, self.node_ready)
        return args

    def node_ready(self, *args):
        print 'node_ready'
        self.ready.set()
        return args

def reactor_running():
    print 'reactor_running'
    df = threads.deferToThread(run_in_thread)
    df.addCallback(print_done)

def run_in_thread():
    print 'run_in_thread'
    for n in processes:
        n.ready.wait()

def print_done(dfResult=None):
    print 'print_done'
    reactor.stop()

if __name__ == '__main__':
    processes = [TestA() for i in range(8)]
    reactor.callWhenRunning(reactor_running)
    reactor.run()

1 个答案:

答案 0 :(得分:10)

简短的回答是肯定的,扭曲和多重处理彼此不兼容,并且您无法在尝试时可靠地使用它们。

在所有POSIX平台上,子进程管理与SIGCHLD处理密切相关。 POSIX信号处理程序是进程全局的,每个信号类型只能有一个。

Twisted和stdlib multiprocessing不能同时安装SIGCHLD处理程序。只有其中一个可以。这意味着只有其中一个可以可靠地管理子进程。你的示例应用程序无法控制它们中的哪一个会赢得这种能力,所以我希望它的行为会产生一些非确定性。

但是,您的示例更直接的问题是您在父进程中加载​​Twisted然后使用multiprocessing来分叉而不是exec 所有子进程。 Twisted不支持像这样使用。如果你分叉然后执行,那就没问题了。但是,缺少新进程的执行程序(可能是使用Twisted的Python进程)会导致Twisted不考虑的各种额外共享状态。在您的特定情况下,导致此问题的共享状态是内部“waker fd”,用于实现deferToThread。在父节点和所有子节点之间共享fd的情况下,当父节点尝试唤醒主线程以传递deferToThread调用的结果时,它很可能会唤醒其中一个子进程而不是 / em>的。子进程没有任何用处,所以这只是浪费时间。同时,父节点中的主线程永远不会唤醒,并且永远不会注意到您的线程任务已完成。

在您已经创建子进程之前,可以通过不加载任何Twisted来避免此问题。就Twisted而言,这会将你的用法转变为单进程用例(在每个进程中,它最初会加载,然后该进程将继续进行fork,所以毫无疑问,fork和Twisted如何相互作用)。这意味着在您创建子进程之前甚至不会导入Twisted。

当然,这只会对Twisted的帮助有所帮助。您使用的任何其他库都可能遇到类似的问题(您提到了glib2,这是另一个库的一个很好的例子,如果你试图像这样使用它会完全窒息)。

我强烈建议您根本不使用multiprocessing模块。相反,使用涉及fork exec的任何多进程方法,而不是单独的fork。安瓿属于这一类。