自从 C# 5 中引入 async/await 以来,开发人员之间一直对 async/await 关键字的最佳实践以及幕后实际发生的事情感到困惑。
让我们先从基础开始。
在 Windows 窗体的早期,UI 延迟与 I/O 操作所花费的时间成正比。这意味着,如果您尝试将数据保存到数据库中,并且数据库调用需要 20 秒,则用户只能等待这 20 秒。这是一个糟糕的用户体验(无论如何,在 2012 年之前谁关心用户体验)。
然后引入了 async/await。现在我们有了一种在两个线程之间划分工作的方法:
-
UI 线程
-
工作线程
主代码在命中 await 关键字后立即由 UI 线程运行。在那一刻,UI线程可以自由地做其他事情,线程池中的另一个线程将接管做繁重的事情。
一旦我们从 DB 获得可用的数据,我们调用 UI 线程来执行 await 关键字之后的下一步指令。
这是用户可以与UI上的其他一些元素进行交互,直到数据被提取。
目前为止,一切都好?
现在让我们一一谈谈快速的最佳实践!
-
**切勿使用 .Wait() 或 .结果:**它将锁定线程,您最终将使用更多线程来完成工作。始终使用 await,或者如果需要进行同步调用,请使用 .GetAwaiter() 中。GetResult()。
-
永远不要将 void 方法标记为异步 — 您需要查看异步方法引发的异常,对吗?如果你将 void 方法标记为异步,你的内部异常将被吞噬,并且(你知道)调试和找到这些异常会很痛苦。
-
始终等待您的异步 — 在没有 await 关键字的情况下使用异步方法没有任何好处。然后是一个同步调用。
-
如果代码有 9/10 次从不使用 await 关键字的热路径(如缓存)获取值,则使用 ValueTask 返回类型而不是 Task。
-
将 IAsyncEnumerable 用于流式处理数据。这意味着,如果存在集合数据巨大并且需要以块形式接收数据的情况,则使用 IAsyncEnumerable。
-
ConfigureAwait — 好吧,我知道这对你来说是一场噩梦,但我会非常简单地解释这一点。如果未使用 ConfigureAwait(false) 方法标记 await 语句,它将调用相同的 UI 线程或工作线程(导致 await 语句的线程)以在 await 之后执行下一组指令。为什么这很糟糕?如果您的工作线程或 UI 线程正忙于执行其他操作,并且在等待后被调用执行下一组语句,该怎么办?
默认情况下,ConfigureAwait(true) 值为 true。需要将其标记为 ConfigureAwait(false)。这将告诉编译器使用 threadpool 中的任何线程在 await 关键字之后执行语句,而不是特别调用 await 语句的线程。
4. 避免返回 await:如果中介类方法只是返回 await 方法,请不要用 async/await 标记它们。除了尝试/捕捉或使用
不良做法:
良好做法:
为什么?:它增加了使用 async/await 关键字的额外复杂性。在后台,编译器将异步方法转换为类。我们希望在中间类中做的就是一个返回任务的方法。调用的方法已经是“异步”和“等待”。我们不需要在每一步都这样做。
ConfigureAwait 仅在使用 SynchronizationContext 的框架中有用。ASP.Net Core 等 Web 应用框架没有 SynchronizationContext。因此,ConfigureAwait true/false 在 Web 应用程序中没有多大用处。
如果你喜欢我的文章,请给我一个赞!谢谢