Multithreading Twisted:同时使用多个线程和进程

Multithreading Twisted:同时使用多个线程和进程,multithreading,unix,twisted,process,reactor,Multithreading,Unix,Twisted,Process,Reactor,Twisted文档让我相信,在同一个应用程序中结合reactor.spawnProcess()和threads.deferToThread()等技术是可以的,reactor会在幕后优雅地处理这个问题。在实际试用时,我发现我的应用程序死锁了。自己使用多个线程,或者自己使用子进程,一切都很好 查看reactor源代码,我发现SelectReactor.spawnProcess()方法只调用os.fork(),而不考虑可能正在运行的多个线程。这就解释了死锁的原因,因为从调用os.fork()开始,您将

Twisted文档让我相信,在同一个应用程序中结合
reactor.spawnProcess()
threads.deferToThread()
等技术是可以的,reactor会在幕后优雅地处理这个问题。在实际试用时,我发现我的应用程序死锁了。自己使用多个线程,或者自己使用子进程,一切都很好

查看reactor源代码,我发现
SelectReactor.spawnProcess()
方法只调用
os.fork()
,而不考虑可能正在运行的多个线程。这就解释了死锁的原因,因为从调用
os.fork()
开始,您将有两个进程,其中多个并发线程正在运行,谁知道用同一个文件描述符做什么

我的问题是,解决这个问题的最佳策略是什么

我想到的是将
SelectReactor
子类化,这样它就是一个单例,并且只调用一次
os.fork()
,在实例化时立即调用。子进程将在后台运行,并充当父进程的服务器(使用管道上的对象序列化来来回通信)。父级继续运行应用程序,并可以根据需要使用线程。父进程中对
spawnProcess()
的调用将委托给子进程,子进程将保证只有一个线程在运行,因此可以安全地调用
os.fork()

以前有人这样做过吗?有没有更快的办法

解决这个问题的最佳策略是什么

(可能在之后)描述问题,最好使用可复制的测试用例(为了最大的准确性)。然后可以讨论实现它的最佳方式(或者不同的平台可能需要不同的解决方案)是什么

以前曾提出过立即创建子进程以帮助进一步创建子进程的想法,以解决围绕子进程收获的性能问题。如果这种方法现在解决了两个问题,那么它看起来就更有吸引力了。这种方法的一个潜在困难是
spawnProcess
同步返回一个对象,该对象提供子对象的PID并允许向其发送信号。如果中间有一个进程,则需要执行更多的工作,因为在
spawnProcess
返回之前,需要将PID传回主进程。类似的挑战将是支持
childFDs
参数,因为不再可能在子进程中仅继承文件描述符


另一种解决方案(可能有点粗俗,但也可能有较少的实现挑战)可能是在调用
os.fork
之前使用非常大的数字调用
sys.setcheckinterval
,然后仅在父进程中恢复原始检查间隔。这应该足以避免进程中的任何线程切换,直到发生
os.execvpe
,从而销毁所有额外的线程。这并不完全正确,因为它会使某些资源(如互斥体和条件)处于不好的状态,但您将这些资源与
延迟线程一起使用并不常见,因此这可能不会影响您的情况。

Jean Paul在回答中给出的建议是好的,但这应该有效(并且在大多数情况下有效)

首先,Twisted也使用线程来解析主机名,我肯定在Twisted进程中使用了子进程,这些子进程也可以建立客户端连接。所以这在实践中是可行的

其次,
fork()
不会在子进程中创建多个线程

应使用单个线程创建进程。如果多线程进程调用fork(),则新进程应包含调用线程的副本

现在,这并不是说
spawnProcess
没有潜在的多线程问题;该标准还规定:

。。。为了避免错误,在调用某个exec函数之前,子进程只能执行异步信号安全操作

我不认为有任何东西可以确保只使用异步信号安全操作

因此,请更具体地说明您的具体问题,因为它不是一个克隆了线程的子进程。

fork()在Linux上肯定只让子进程有一个线程


我假设您知道,在Twisted中使用线程时,允许线程调用的唯一TwistedAPI是callFromThread?所有其他Twisted API必须只能从主反应器线程调用。

过了一段时间回到这个问题,我发现如果我这样做:

reactor.callFromThread(reactor.spawnProcess,*spawnargs)

与此相反:

reactor.spawnProcess(*spawnargs)

然后这个问题在我的小测试用例中消失了。Twisted文档“使用进程”中有一句话让我尝试这样做:“Twisted中的大多数代码都不是线程安全的。例如,从协议向传输写入数据不是线程安全的。”


我怀疑Jean Paul提到的其他有这个问题的人可能也犯了类似的错误。应用程序的责任是强制在正确的线程中执行reactor和其他API调用。显然,除了极少数例外,“正确的线程”几乎总是主要的反应堆线程。

谢谢你的链接;我不知道只复制了调用fork()的线程。然而,我不相信我所做的“应该”有两个原因。首先,我听说GNU/Linux不完全符合Pthreads,因此标准中的一些语义可能无法为我实现。第二