这可能是重复的,但我找不到明确的答案。是的,许多食谱都是旧版本的.NET Framework
,但在现代版本中.NET (Core)
可能会发生一些变化。
因此,遗留代码包含如下结构:
FooAsync().Wait();
...
var result = BarAsync().Result;
在我看来,这种对异步方法的处理是不正确的。在某个地方,我设法将异步拖到所有级别,并通过await
. 但不可能在所有地方都这样做;在顶层的某个地方,一切都依赖于同步代码,我不确定是否可以将其更改为异步。或者在更高的地方有很多层次的架构,我也无法一次全部改变。
那么,问题是:在现代版本中.NET
如何建议从同步代码调用异步代码?我知道根本不建议这样做,但是对于无法避免这种情况的情况,最正确的方法是什么?还是只能接受现状,其他选择也好不到哪儿去?
PS 特别是,在类构造函数中调用异步方法存在问题。是的,有一些技术可以隐藏构造函数,但在我看来,它们不能与使用DI
框架结合起来。例子:
public class Foo : IFoo
{
public Foo(ISomething something, IAnother another)
{
_something = something;
_another = another;
Bar().Wait();
}
}
...
serviceCollection.AddSingleton<ISomething, Something>();
serviceCollection.AddSingleton<IAnother, Another>();
serviceCollection.AddSingleton<IFoo, Foo>();
事实上,异步同步唯一有效和使用的地方是
main
应用程序的入口点。但为了不让任何人感到困惑,.NET 开发人员已经实现了绑定,现在您可以简单地编写。这就是顶层。没有其他上层。
当然,例如 Windows 窗体,其中底层事件处理程序不支持异步,例如
但这里已经有现成的解决方案了。由于异步方法在运行时不会阻塞 UI,因此您应该保护按钮直到异步操作结束。另外,由于除了
void
从方法返回之外无法返回任何内容,因此需要捕获async void
方法中的异常。事实上,这就是整个解决方案。根据定义,代码中不应该有任何
.Wait()
or 。.Result
唯一允许提及
.Result
它的时候是它Task
已经明确完成的时候,甚至在某些情况下这被认为是比 更健康的做法await
,但这些都是微妙之处。因此,如果你知道自己在做什么,那么你可以将其用作临时拐杖,
.Wait()
直到.Result
你系统地完全重写它。在任何情况下都强烈建议不要仅仅因为似乎没有其他简单的解决方案而使用同步异步。因为在最好的情况下,这将是由于无意义的流程阻塞而浪费资源,在最坏的情况下,您将寻找仅在行星巡游期间满月时发生的死锁。
PS 对于设计师来说,也有几种解决方案。其中一人“开枪后就忘记了”。
现在我们需要处理使用它的方法中出现错误的概率
_superImportantValue
。选项 1 - 抛出异常
选项 2 - 仍然冻结
如果逻辑上假设在 99.9% 的情况下
_initTask
该方法在调用方法时已经准备好,则此选项是可以接受的。无论如何,如果这不是一个理想的解决方案,那么至少它不会阻止您在 IOC 容器中构建应用程序。
选项 3 - 有可能吗?