Python twisted是否与多处理事件和队列不兼容?

Python twisted是否与多处理事件和队列不兼容?,python,twisted,multiprocessing,Python,Twisted,Multiprocessing,我试图模拟使用twisted运行的应用程序网络。作为模拟的一部分,我希望同步某些事件,并能够为每个进程提供大量数据。我决定使用多处理事件和队列。然而,我的流程正在被挂起 我编写了下面的示例代码来说明这个问题。具体地说,(在我的sandy bridge机器上大约95%的时间),在线程中运行函数完成,但是在我按下Ctrl-C之后才调用“print\u done”回调。 此外,我可以更改示例代码中的一些内容,使其工作更可靠,例如:减少派生进程的数量,从reactor_ready调用self.ready

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

我编写了下面的示例代码来说明这个问题。具体地说,(在我的sandy bridge机器上大约95%的时间),在线程中运行函数完成,但是在我按下Ctrl-C之后才调用“print\u done”回调。

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

我猜twisted reactor和阻塞多处理调用(如Queue.get()或Event.wait())之间存在竞争条件

我到底遇到了什么问题?我的代码中是否有我遗漏的错误?我是否可以修复此问题,或者与多处理事件/队列不兼容

第二,像spawnProcess或安瓿这样的东西是推荐的替代品吗?(如中所述)

编辑(按要求):


我试过的所有反应器都出现了问题,包括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

除了我的sandy bridge机器,我在我的双核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()

简单的回答是肯定的,Twisted和多处理互不兼容,您无法在尝试使用它们时可靠地使用它们

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

Twisted和stdlib
多处理
不能同时安装
SIGCHLD
处理程序。他们中只有一个能。这意味着它们中只有一个可以可靠地管理子进程。您的示例应用程序无法控制它们中的哪一个将获得该能力,因此我希望在其行为中会出现一些由该事实引起的非确定性

但是,您的示例更直接的问题是,您在父进程中加载Twisted,然后使用
多处理
来分叉而不是执行所有子进程。Twisted不支持这样使用。如果你用fork然后exec,就没有问题了。然而,新进程(可能是使用Twisted的Python进程)的exec的缺乏导致了Twisted没有考虑的各种额外共享状态。在您的特定情况下,导致此问题的共享状态是内部“waker fd”,用于实现
deferToThread
。当父线程和所有子线程共享fd时,当父线程试图唤醒主线程以传递
deferToThread
调用的结果时,它很可能会唤醒其中一个子进程。子进程没有什么有用的事情可做,所以这只是浪费时间。同时,父线程中的主线程从未唤醒,也从未注意到线程任务已完成

通过在创建子进程之前不加载任何Twisted,可以避免此问题。就Twisted而言,这将把您的使用转化为一个单一的流程用例(在每个流程中,它将首先被加载,然后该流程将根本不会进入fork,因此fork和Twisted之间不再存在如何交互的问题)。这意味着在创建子进程之前,甚至不导入Twisted

当然,这只会帮助你走出困境。您使用的任何其他库都可能遇到类似的问题(您提到glib2,这是另一个库的一个很好的例子,如果您尝试这样使用它,它将完全阻塞)


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

您使用的Twisted的确切细节、您运行的操作系统、您使用的反应器(等等)将有助于回答这个问题。我尝试过的glib2reactor selectreactor、pollreactor和epollreactor的所有反应器都出现了问题。我正在运行GentooLinux、Python2.7,并尝试了Twisted11.1.0、12.0.0和12.1.0。感谢您的详细解释。现在所有的问题都很明显了!我使用subprocess.Popen和multiprocessing.connection侦听器/客户端(使用AF_UNIX)使模拟运行良好,以促进扭曲节点和控制器进程之间的通信/同步。再次感谢!