扭曲的文档使我相信结合诸如以下技术是可以的 reactor.spawnProcess()threads.deferToThread() 在同一个应用程序中,反应堆将在盖子下优雅地处理这个问题。在实际尝试时,我发现我的应用程序死锁。自己使用多个线程,或者自己使用子进程,一切都很好。

调查反应堆的源头,我发现 SelectReactor.spawnProcess() 方法简单调用 os.fork() 没有任何考虑可能正在运行的多个线程。这解释了死锁,因为从调用 os.fork() 您将有两个进程与多个并发线程运行和做谁知道什么与相同的文件描述符。

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

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

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

有帮助吗?

解决方案 3

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

reactor.callFromThread(reactor.spawnProcess, *spawnargs)

而不是这个:

reactor.spawnProcess(*spawnargs)

然后问题在我的小测试用例中消失。扭曲的文档"使用进程"中有一段话导致我尝试这个:"Twisted中的大多数代码都不是线程安全的。例如,从协议向传输写入数据不是线程安全的。"

我怀疑Jean-Paul提到的其他人有这个问题可能正在犯类似的错误。应用程序有责任强制执行reactor和其他API调用正在正确的线程中进行。显然,除了非常狭窄的例外,"正确的线程"几乎总是主反应器线程。

其他提示

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

申请机票 (也许之后 登记)描述问题,最好用可重复的测试用例(以获得最大的准确性)。然后可以讨论实现它的最佳方式(或方式-不同的平台可能需要不同的解决方案)可能是什么。

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

另一种解决方案(可能更具黑客攻击性,但也可能具有更少的实现挑战)可能是调用 sys.setcheckinterval 在打电话之前有一个很大的号码 os.fork, ,然后仅在父进程中恢复原始检查间隔。这应该足以避免进程中的任何线程切换,直到 os.execvpe 发生,破坏所有额外的线程。这并不完全正确,因为它会使某些资源(如互斥体和条件)处于不良状态,但是您使用这些资源时 deferToThread 不是很常见,所以也许这不会影响你的情况。

让-保罗在他的回答中给出的建议是好的,但这 应该 工作(并且在大多数情况下确实如此)。

首先,Twisted也使用线程进行主机名解析,并且我肯定在Twisted进程中使用子流程,这些进程也可以进行客户端连接。所以这可以在实践中工作。

第二, fork() 不会在子进程中创建多个线程。 根据标准描述 fork(),

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

这并不是说有 非也。 潜在的多线程问题 spawnProcess;标准还说:

...为了避免错误,子进程只能执行异步信号安全操作,直到调用其中一个exec函数为止。..

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

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

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

我假设你知道,当在Twisted中使用线程时,线程被允许调用的唯一Twisted API是callFromThread?所有其他扭曲的Api只能从主,反应器线程调用。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top