迟到总比没有好!!!
Java 在尝试提出可以并行执行的更轻量级的执行单元方面晚了 10 多年。附带说明一下,Project loom 还旨在在 Java 中公开“定界延续”,我相信这只不过是 C# 的古老“yield”关键字(又迟了将近 20 年!!)
Java 确实认识到需要解决由 asyn await 解决的更大问题(或者实际上是 C# 中的任务,这是一个大想法。Async Await 更像是一种语法糖。非常重要的改进,但仍然不是解决问题的必要条件操作系统映射线程的实际问题比预期的要重)。
在此处查看有关项目织机的提案:https://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html
并导航到最后一部分“其他方法”。你会明白为什么 Java 不想引入 async/await。
话虽如此,我并不真正同意所提供的推理。在这个提议和斯蒂芬的回答中都没有。
首先让我们诊断一下斯蒂芬的答案
- async await 解决了上面提到的第 1 点。 (Stephan 也进一步承认了这一点)
- 这对框架和工具来说肯定是额外的工作,但对程序员来说根本不是。即使使用异步等待,.Net 调试器在这方面也相当出色。
- 我只是部分同意。 async await 的全部目的是优雅地混合异步世界和同步结构。但是,是的,您要么需要将调用者也声明为异步,要么直接在调用者例程中处理 Task。但是,project loom 也不会以有意义的方式解决它。为了充分受益于轻量级虚拟线程,即使是调用例程也必须在虚拟线程上执行。不然有什么好处?你最终会阻塞一个操作系统支持的线程!!!因此,即使是虚拟线程也需要在代码中具有“病毒性”。相反,在 Java 中更容易注意到您正在调用的例程是异步的并且会阻塞调用线程(如果调用例程本身不在虚拟线程上执行,这将是令人担忧的)。 C# 中的 Async 关键字使意图非常明确并迫使您做出决定(如果您想通过询问 Task.Result 来阻止,C# 中也可以阻止。大多数情况下,调用例程本身也可以很容易地异步)。
Stephan 说需要异步编程来防止 (OS) 线程阻塞是正确的,因为 (OS) 线程很昂贵。这正是需要虚拟线程(或 C# 任务)的全部原因。您应该能够在不失眠的情况下“阻止”这些任务。为了不失去睡眠,调用例程本身应该是一个任务,或者阻塞应该在非阻塞 IO 上,框架足够聪明,不会在这种情况下阻塞调用线程(继续的力量)。
C# 支持这一点,并且提议的 Java 特性旨在支持这一点。
根据提议的 Java api,阻塞虚拟线程将需要在 Java 中调用 vThread.join() 方法。
真的比调用 await workDoneByVThread() 更有益吗?
现在让我们看看项目织机提案推理
在 async/await 很容易用 continuation 实现的意义上,Continuations 和 Fiber 支配 async/await(实际上,它可以用一种称为 stackless continuation 的弱分隔延续形式来实现,它不会捕获整个调用-stack 但仅是单个子程序的本地上下文),反之则不行
我不明白这个说法。如果有人这样做,请在 cmets 中告诉我。
对我来说,异步/等待是使用延续实现的,就堆栈跟踪而言,由于纤程/虚拟线程/任务位于虚拟机中,因此必须可以管理该方面。事实上 .net 工具确实可以做到这一点。
虽然 async/await 使代码更简单,并使它看起来像正常的顺序代码,就像异步代码一样,但它仍然需要对现有代码进行重大更改,库中的明确支持,并且不能与同步代码很好地互操作
我已经介绍过了。不对现有代码进行重大更改并且在库中没有明确支持实际上意味着无法有效地使用此功能。除非 Java 的目标是透明地将所有线程转换为虚拟线程,但它不能也不是,这句话对我来说没有意义。
作为一个核心思想,我发现 Java 虚拟线程和 C# 任务之间没有真正的区别。以至于项目 loom 也将工作窃取调度程序作为默认目标,与 .Net 默认使用的调度程序相同(https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskscheduler?view=net-5.0,滚动到最后的备注部分)。
似乎唯一的争论是应该采用什么语法来消费这些。
采用C#
- 与现有线程相比,一个独特的类和接口
- 非常有用的语法糖,用于将异步与同步结合起来
Java 的目标是:
- 同样熟悉的 Java Thread 界面
- 除了对 ExecutorService 的 try-with-resources 支持之外没有任何特殊结构,因此可以自动等待提交的任务/虚拟线程的结果(从而阻塞调用线程,虚拟/非虚拟)。
恕我直言,Java 的选择比 C# 更糟糕。拥有一个单独的接口和类实际上非常清楚地表明行为有很大不同。当程序员没有意识到她现在正在处理不同的东西时,或者当库实现更改以利用新结构但最终阻塞调用(非虚拟)线程时,保留相同的旧接口可能会导致细微的错误。
此外,没有特殊的语言语法意味着阅读异步代码仍然难以理解和推理(我不知道为什么 Java 认为程序员爱上了 Java 的线程语法,他们会很高兴知道这一点而不是编写同步寻找代码,他们将使用可爱的 Thread 类)
哎呀,现在甚至 Javascript 也有异步等待(具有所有的“单线程”)。