如何解决项目织机:使用虚拟线程时,什么可以使性能更好? 编辑
为了在这里提供一些背景信息,我已经关注项目织机一段时间了。我已经读过the state of loom。我已经完成了异步编程。
异步编程(由java nio提供)在任务等待时将线程返回到线程池,并且不中断线程会花很长的时间。这样可以大大提高性能,我们现在可以处理更多的请求,因为它们不受操作系统线程数的直接限制。但是我们在这里失去的是背景。现在,同一任务不再仅与一个线程相关联。一旦我们将任务与线程分离,所有上下文都会丢失。异常跟踪不能提供非常有用的信息,并且调试很困难。
带有virtual threads
的项目织机进入其中,成为单个并发单元。现在,您可以在单个virtual thread
上执行单个任务。
到目前为止一切都很好,但是文章继续讲到项目织机:
简单,同步的Web服务器将能够处理更多请求,而无需更多硬件。
我不明白我们如何通过织机获得异步API带来的性能优势? asynchrounous APIs
确保不要使任何线程空闲。那么,项目织机如何使asynchronous
API更加高效和高性能呢?
编辑
让我重新提出问题。假设我们有一个http服务器,该服务器接受请求并使用支持的持久数据库进行一些操作。说,这个http服务器处理许多请求-100K RPM。两种实现方法:
- HTTP服务器具有专用的线程池。当请求进入时,线程将任务继续进行直到到达数据库为止,其中任务必须等待来自数据库的响应。此时,线程将返回到线程池并继续执行其他任务。当DB响应时,它再次由线程池中的某个线程处理,并返回HTTP响应。
- HTTP服务器仅为每个请求生成
virtual threads
。如果有IO,则虚拟线程仅等待任务完成。然后返回HTTP响应。基本上,virtual threads
没有任何合并业务。
鉴于硬件和吞吐量保持不变,在响应时间或处理更多吞吐量方面,任何一种解决方案的性能都优于另一种吗?
我的猜测是,性能不会有任何差异。
解决方法
我们没有从异步API中受益。我们可能会获得的性能类似于异步,但具有同步代码。
,-
http服务器具有专用的线程池...。 一个游泳池有多大? (CPU数量)* N + C? N> 1可能会退回到反扩展,因为锁争用会延长延迟。当N = 1时,可能无法充分利用可用带宽。有一个很好的分析here。
-
http服务器刚刚生成... 那将是这个概念非常幼稚的实现。一种更现实的方法是努力从动态池中收集数据,该动态池为每个阻塞的系统调用保留一个实际线程,为每个实际CPU保留一个实际线程。至少这是Go背后的人们想到的。
关键是要避免{处理程序,回调,补全,虚拟线程,goroutine:吊舱中的所有 PEA }争夺内部资源;因此,除非绝对必要,否则它们不会依赖基于系统的阻止机制。这属于避免锁的旗帜,并且可以通过各种排队策略(请参见libdispatch)等来实现。 PEA 与底层系统线程分离,因为它们在它们之间进行了内部多路复用。这是您对概念离婚的关注。在实践中,您会传递自己喜欢的上下文指针抽象语言。
正如1所指出的,有切实的成果可以直接与这种方法联系起来;和一些无形的东西。锁定很容易-您只需对交易进行大笔锁定,就可以开始交易了。那没有规模;但是细粒度的锁定很难。很难工作,很难选择谷物的细度。在教科书示例中,何时使用{锁,CV,信号量,障碍...}是显而易见的;在深层嵌套的逻辑中要少一些。避免锁使得它在很大程度上消失了,并仅限于诸如malloc()之类的竞争叶子组件。
我对此持怀疑态度,因为该研究通常显示出一个伸缩性差的系统,该系统被转换为锁避免模型,然后证明是更好的模型。我还没有看到可以释放一些经验丰富的开发人员来分析系统的同步行为,对其进行转换以实现可伸缩性,然后测量结果的人。但是,即使这是一次胜利,有经验的开发人员也是一种稀有(昂贵)的商品;可扩展性的核心确实是财务。
,@talex的answer简明扼要。进一步添加。
Loom更多地是关于本机并发抽象的,它还可以帮助编写异步代码。鉴于它具有VM级别的抽象性,而不仅仅是代码级别(如我们迄今为止使用CompletableFuture
等所做的工作),它可以实现异步行为,但可以减少样板。
使用Loom,一个更强大的抽象是救世主。关于语法糖的抽象如何使一个人有效地编写程序,我们已经反复看到了这一点。无论是JDK8中的FunctionalInterfaces,还是Scala中的理解。
使用织机,无需链接多个CompletableFuture(以节省资源)。但是人们可以同步编写代码。随着遇到的每个阻塞操作(ReentrantLock,I / O,JDBC调用),虚拟线程将被驻留。而且由于这些线程是轻量级线程,因此上下文切换非常便宜,将自己与内核线程区分开来。
被阻止时,实际的载线程(正在运行虚拟线程的run
主体)将参与执行其他虚拟线程的运行。如此有效,载波线程并不处于空闲状态,而是在执行其他一些工作。并在退出时返回以继续执行原始虚拟线程。就像线程池如何工作一样。但是在这里,您只有一个载波线程,可以执行多个虚拟线程的主体,并在阻塞时从一个虚拟线程切换到另一个虚拟线程。
我们得到与手动编写的异步代码相同的行为(并因此获得了性能),但是避免了样板操作。
考虑网络框架的情况,其中有一个单独的线程池来处理I / O,另一个用于执行http请求。对于简单的HTTP请求,可能会从http-pool线程本身提供请求。但是,如果存在任何阻塞(或)高CPU操作的情况,我们会让此活动异步发生在单独的线程上。
此线程将从传入的请求中收集信息,生成CompletableFuture
,并使用管道将其链接(从数据库读取为一个阶段,然后从中进行计算,然后再进行另一阶段写回)数据库案例,Web服务调用等)。每个阶段都是一个阶段,结果CompletablFuture
返回到网络框架。
最终的结果完成后,网络框架将使用结果将结果中继回客户端。 Play-Framework
和其他人就是这样处理的。在http线程处理池和每个请求的执行之间提供隔离。但是,如果我们深入研究这个问题,为什么这样做呢?
一个核心原因是要有效地利用资源。特别是阻止通话。因此,我们使用thenApply
等进行链接,这样就不会在任何活动中阻塞任何线程,并且在线程数较少的情况下可以做更多的事情。
这很好用,但冗长。而且调试确实是很痛苦的,并且如果其中一个中间阶段的结果异常,那么控制流程将变得一团糟,从而导致进一步的代码来处理它。
使用Loom,我们可以编写同步代码,并让其他人决定被阻止时该怎么做。而不是睡觉什么也不做。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。