基础知识 |线程与反应式并发模型



我是响应式编程世界的新手。我正在研究 Akka 演员作为玩的开始步骤。

我对基于线程的并发模型的理解是(例如基于Vanilla Servlet的模型):

  1. 对于来自客户端的每个请求,都会生成一个新线程。
  2. 这意味着所有底层代码的执行都附加到此线程并串行发生。显然,即使代码的一部分存在瓶颈(远程 Web 服务调用等),线程也会被阻塞并等待并保持空闲状态。
  3. 服务器(容器)有一个固定的线程池,以适应最大数量的并发线程,显然,由于瓶颈,它们会快速运行线程。

我对反应式并发模型的理解是(例如基于 Akka 的模型):

  1. 所有逻辑不再附加到单个线程并串行执行。
  2. 执行流是反应性的(即在消息上,触发参与者)。

现在我的问题:

假设远程 Web 服务调用的瓶颈存在于两个模型中。参与者模型如何帮助提高 CPU/内核利用率?我们不会有同样的问题,即Actor中的执行线程被阻塞吗?例如:如果有 200 个参与者同时被此 Web 服务调用阻止?不是说当前有 200 个线程被阻塞吗?我知道仍然会有其他行为者对其他上游事件做出反应。这就是我们所说的更好地利用 CPU 吗?

在线程模型中,只有线程池的小尺寸是问题的原因吗?

Actor 子系统是否没有一个线程池来生成新的 Actor 并对特定事件做出反应?如果是,那么我们没有同样的问题吗?

如果这个问题完全愚蠢,请原谅我。

"假设瓶颈..."——在反应式模型中,你永远不会使用这样的服务,而是异步调用 Web 服务的请求,并为最终响应/错误配置处理程序。如果调用同步服务,则只会重新引入所有批处理模式问题,从而使问题更加复杂。 如果您不能直接使用一个,您将创建一个类似代理的服务作为苍白的模仿 (*)。

当响应到达时,处理程序可以解包继续操作所需的任何上下文。 如果所述上下文相当于"堆栈+寄存器",那么您的框架不是很好,但加载比拥有数百个内核线程上下文来解析消息要好。

必须构建响应上下文的形式主义应该引导解决方案远离资源争夺,这在线程模型中很常见。 这是一个吸引力,因为良好的线程和较差的反应式解决方案都是可能的,如果违反直觉的话。

回顾:应用程序空间中的小数据结构包含继续操作所需的信息,比内核线程 + 用户线程 + 堆栈要好得多。 不仅仅是因为它使用更少的资源;但是因为它更好地表达了您的解决方案,并允许底层框架/操作系统/内核/...根据比"返回地址"更多的信息对事件调度进行排序。

自然反应性问题应该有自然

反应式解决方案,就像自然面向批处理的问题应该有自然面向批处理的解决方案一样。

(*)= 几乎所有的响应式框架都是建立在传统的同步内核之上的,比如Linux;反应式内核正在不断发展,大型多处理器的到来将有助于这些概念的主流化。

Actor 是否可以提供帮助取决于参与者是如何实现的。如果它们确实是单独的线程,则如您所说,未在 Web 服务上阻止的执行组件将继续执行。因此,即使 Web 服务阻止了其中一些工作,也会继续完成有用的工作。

如果增加线程池的大小,也是如此。他们中的一些人将做其他有用的工作。

拥有 200 个线程意味着您现在给底层操作系统带来了更多的负担。很多人会惊恐地反应过来。但是,值得研究一下"操作系统的负担"实际上是什么。这是一点点记忆,仅此而已。对于在 Web 服务上被阻止的线程,它们被阻止,它们没有被调度,因此它们不会增加系统的上下文切换负担(上下文切换是性能杀手;在大量线程之间切换会消耗大量时间)。因此,它们对系统性能的危害相对较小(前提是您不是动态生成Actor)。

因此,在这两种方法中,您都需要足够的线程(Actor或线程池线程),以便有一个合理的线程数来保持Web服务的100%利用率,并且有足够的线程来执行其他任务以使本地计算机也保持繁忙。您希望刚好足以使两台计算机饱和,但不要达到本地计算机同时处理太多就绪线程的程度。

现在,每个网络、Web 服务、主机都是不同的,性能是不断变化的。您最终将编写代码来动态控制线程池的大小或准备启动的参与者数量。这可能很繁琐...

最新更新