.NET 8 的 green thread 异步模型被搁置了

.NET 平台上的green thread 异步模型实验结果最近出来了,具体参见:https://github.com/dotnet/runtimelab/issues/2398 ,实验结果总结一下就是在.NET和 ASP.NET Core中实现Green Thread是可行的。Green Thread 在.NET运行时环境中的基本成本和好处,以及与异步编程模型的交互和挑战。如果引入了全新的异步编程模型,对于.NET开发人员来说,Green Thread 和现有异步模型async/await 之间的交互非常复杂。因此,决定暂停绿色线程试验,继续改进现有的async/await模型,以便在.NET中开发异步代码。

文章对为什么要进行Green thread的实验的总结一下就这么几点:

  1. .NET的异步编程模型简化了应用程序的异步代码编写,对于增强I/O绑定方案的可伸缩性非常关键。
  2. I/O绑定代码经常处于等待状态,如等待网络返回数据。异步代码提高了可伸缩性,显著降低了等待I/O的请求成本。
  3. 异步C#代码的优势是在等待I/O操作时的低成本,并且允许服务器并行处理大量请求。
  4. 但异步编码也有挑战,因为开发者需要确定哪些方法应该异步化。全面异步化不现实,因为异步方法有性能、类型限制,并且编程模型复杂。
  5. Green thread的目的是简化编程模型,使得所有代码可以以同步方式编写,但仍保持可伸缩性和性能。
  6. Green thread在其他编程环境中已经被验证为有效,现在的考虑是它是否适用于C#,特别是考虑到存在的async/await模型。

文章里也对搁置Green thread的结论总结几点:

  1. Green thread为.NET开发人员提供了一个全新的异步编程模型。 asp. net core benchmark 显示 green thread 性能不如现有的 async/await,async/await 达到 178,620 rps 的同时 green thread 只达到了 162,019 rps, .NET 平台是目前为止唯一一个同时实现了Green Thread 和async/await 异步模型的平台,这就让我们有了一个横向比较两种编程模型的平台,这也就破案了在社区中 异步编程模型哪个更快的争论,这里有个非常好的面试题就说 golang,nodejs,java等等他们实现的异步编程模型分别是哪一种,他们有什么优缺点等。
  2. Green thread与现有的异步模型之间的交互是复杂的。特别是从Green thread代码调用异步方法需要转换到异步代码的同步模式,这在常规线程上不是一个好的选择。 micro benchmark 显示深 green thread 调用栈的性能远不如深 async/await 调用链。
  3. 在Green thread模型中,与本机代码的互操作性是复杂和相对较慢的。基于P/Invoke的基准测试显示,Green thread上的操作成本明显增加。 100,000,000 次 P/Invoke 从原来的 300ms 变成需要 1800ms。
  4. Green thread在与某些特定特性如线程局部静态变量和本机线程状态交互时存在功能上的问题。 thread local 变量的支持以及暴露 native thread 状态变得非常难以实现。
  5. Green thread与某些安全缓解措施,如防止面向返回的编程的影子堆栈( shadow stacks),的交互是具有挑战性的。
  6. 在某些关键场景中,Green thread模型的速度有可能超过异步,但这种性能提升的代价是其他场景下的性能下降,以及需要放弃一些兼容性和特性。
  7. 一个未解之谜是,通过优化异步,是否可以让Green thread在性能上超过异步。
  8. 开发团队发现以上问题在其它使用 green thread 的语言中同样存在。

文章后面的讨论值得看一看,其中**rcollette** 的观点特别有意思:https://github.com/dotnet/runtimelab/issues/2398#issuecomment-1713003525

这篇关于loom/Java 21的演讲对于那些希望深入了解绿色线程的人来说非常不错。 https://blog.jetbrains.com/idea/2023/05/new-livestream-virtual-threads-and-structured-concurrency-in-java-2021-with-loom/

我预计在现实世界中,它们(对现有代码)有益的情境会非常有限。你需要大量的阻塞IO,对吗?到线程池饥饿成为一个问题的程度。

在Java世界中,这很快就会发生,原因有以下几点:

  1. Java没有标准的非阻塞数据库驱动规范。Java在开始研究绿色线程之前应该先解决这个问题。容易说"你的操作持续时间太长",但有些事务本质上运行时间很长,并且有时候你无法控制。这是主要的问题。
  2. 对于一些开发人员来说,反应式异步模式/API是一个心智跳跃,他们只是试图避免它(并不是说这是对的,但这种情况经常发生)。这比JS中的Promise嵌套还要糟糕。对于你使用的每一个方法,你都必须考虑我是否返回相同的类型,我是否返回另一个promise(Future),我是处理一个集合还是单个值,都需要不同的方法调用,等等。你还会遇到线程上下文的情况,比如事务,日志MDC等,在反应式模型中似乎毫无理由地失败,这再次让开发人员失去信心。说"他们是开发人员,他们应该做得对或离开这个行业"都把责任推到了平台开发者身上来提供优雅的解决方案。这是Java存在的一个问题,并且坦白说,我不希望这种情况在.NET中发生,因为Java中这种不够优雅的原因正是我更喜欢.NET的原因。