异步不是线程
2024-03-17 17:33:41
我和很多人一样,刚学着使用async
方法时以为是会新开一个线程并执行代码。心想有了这玩意还要Thread
、Task
干嘛。。。
而实时并非如此。
线程的问题就是会造成 CPU 资源的浪费。
1 | private void button1_Click(object sender, EventArgs e) |
由于这个方法是在 UI 线程上执行的,所以整个线程被冻结了,导致界面无法响应。在此期间该线程无事可做,这就是资源的浪费。
如果用异步就不同了:
1 | private async void button1_Click(object sender, EventArgs e) |
当遇到 await 表达式时,它会暂停当前方法的执行,并返回控制权给调用者,使得线程可以执行其他工作,比如响应 UI。
一旦可等待对象操作完成,await 表达式就会返回并继续执行后面的代码。
注意,await 后面的代码可能会与之前的代码运行在不同的线程上,这取决于SynchronizationContext
。
1 | private static async Task TestAync() |
总结
异步模型解决了在此前开发中的两个痛点:
- 解决传统的基于回调的异步编程所带来的一系列问题。使得在异步代码中可以使用类似同步代码的方式来编写异步操作,避免了回调地狱。
- 更好地利用线程,
await
关键字可以使应用程序能够处理更多的并发操作,提高了应用程序的并发性能和吞吐量。
async
方法可以通过返回Task
、Task<T>
或void
来标记。返回类型为Task
或Task<T>
的方法可以实现异步操作,并在操作完成时提供结果或状态。返回类型为void
的方法通常用于事件处理程序等不需要提供结果的情况。async
方法不会开启新线程。async
方法本质上是一个状态机,它可以在执行过程中暂停和恢复,而不会阻塞调用线程。await
会让出当前线程的 CPU 时间片,以允许其他任务在可用的线程上执行。await
之前的代码和之后的代码可能不在同一个线程上,这取决于上下文和调度器。- 可以使用
ConfigureAwait(false)
来禁用上下文捕获,以避免不必要的线程切换。这在某些情况下可以提高性能,特别是在不需要 UI 上下文的非 UI 代码中。 - 异步方法应该遵循一致的命名约定,以便清楚地表示其异步特性,例如在方法名后添加”Async”后缀。
参考
Async and Await
If async-await doesn’t create any additional threads, then how does it make applications responsive?