当前位置: 首页 > news >正文

C# 异步编程场景

前言

异步编程允许程序在等待某些操作(如文件读写、网络请求等)完成时,不必阻塞主线程,从而可以继续执行其他任务。这种非阻塞的特性对于提高应用程序的并发性和响应速度至关重要。C# 通过 asyncawait 关键字,以及 Task 类,为异步编程提供了简洁而强大的支持。

本文将深入探讨 C# 中的异步编程场景,包括其基本概念、工作原理以及如何在实际开发中应用异步编程技术。我们将通过实例演示如何利用 C# 的异步特性来编写高效、响应迅速的应用程序,同时也会讨论在异步编程中可能遇到的一些常见问题及其解决方案。

如果需要 I/O 绑定(例如从网络请求数据、访问数据库或读取和写入到文件系统),则需要利用异步编程。 还可以使用 CPU 绑定代码(例如执行成本高昂的计算),对编写异步代码而言,这是一个不错的方案。

C# 拥有语言级别的异步编程模型,让你能轻松编写异步代码,而无需应付回调或受限于支持异步的库。 它遵循基于任务的异步模式 (TAP)。

异步模型概述

异步编程的核心是 Task 和 Task<T> 对象,这两个对象对异步操作建模。 它们受关键字 async 和 await 的支持。 在大多数情况下模型十分简单:

  • 对于 I/O 绑定代码,等待一个在 async 方法中返回 Task 或 Task<T> 的操作。
  • 对于 CPU 绑定代码,等待一个使用 Task.Run 方法在后台线程启动的操作。

await 关键字有这奇妙的作用。 它控制执行 await 的方法的调用方,且它最终允许 UI 具有响应性或服务具有灵活性。 虽然有方法可处理 async 和 await 以外的异步代码,但本文重点介绍语言级构造。

 备注

在以下一些示例中,System.Net.Http.HttpClient 类用于从 Web 服务下载某些数据。 这些示例中使用的 s_httpClient 对象是 Program 类的静态字段(请检查完整示例):

private static readonly HttpClient s_httpClient = new();

I/O 绑定示例:从 Web 服务下载数据

你可能需要在按下按钮时从 Web 服务下载某些数据,但不希望阻止 UI 线程。 可执行如下操作来实现:

C#复制

s_downloadButton.Clicked += async (o, e) =>
{// This line will yield control to the UI as the request// from the web service is happening.//// The UI thread is now free to perform other work.var stringData = await s_httpClient.GetStringAsync(URL);DoSomethingWithData(stringData);
};

代码表示目的(异步下载数据),而不会在与 Task 对象的交互中停滞。

CPU 绑定示例:为游戏执行计算

假设你正在编写一个移动游戏,在该游戏中,按下某个按钮将会对屏幕中的许多敌人造成伤害。 执行伤害计算的开销可能极大,而且在 UI 线程中执行计算有可能使游戏在计算执行过程中暂停!

此问题的最佳解决方法是启动一个后台线程,它使用 Task.Run 执行工作,并使用 await 等待其结果。 这可确保在执行工作时 UI 能流畅运行。

static DamageResult CalculateDamageDone()
{return new DamageResult(){// Code omitted://// Does an expensive calculation and returns// the result of that calculation.};
}s_calculateButton.Clicked += async (o, e) =>
{// This line will yield control to the UI while CalculateDamageDone()// performs its work. The UI thread is free to perform other work.var damageResult = await Task.Run(() => CalculateDamageDone());DisplayDamage(damageResult);
};

此代码清楚地表达了按钮的单击事件的目的,它无需手动管理后台线程,而是通过非阻止性的方式来实现。

内部原理

在 C# 方面,编译器将代码转换为状态机,它将跟踪类似以下内容:到达 await 时暂停执行以及后台作业完成时继续执行。

从理论上讲,这是异步的承诺模型的实现。

需了解的要点

  • 异步代码可用于 I/O 绑定和 CPU 绑定代码,但在每个方案中有所不同。
  • 异步代码使用 Task<T> 和 Task,它们是对后台所完成的工作进行建模的结构。
  • async 关键字将方法转换为异步方法,这使你能在其正文中使用 await 关键字。
  • 应用 await 关键字后,它将挂起调用方法,并将控制权返还给调用方,直到等待的任务完成。
  • 仅允许在异步方法中使用 await

识别 CPU 绑定和 I/O 绑定工作

本指南的前两个示例演示如何将 async 和 await 用于 I/O 绑定和 CPU 绑定工作。 确定所需执行的操作是 I/O 绑定或 CPU 绑定是关键,因为这会极大影响代码性能,并可能导致某些构造的误用。

以下是编写代码前应考虑的两个问题:

1、代码是否会“等待”某些内容,例如数据库中的数据?

如果答案为“是”,则你的工作是 I/O 绑定

2、代码是否要执行开销巨大的计算?

如果答案为“是”,则你的工作是 CPU 绑定

如果你的工作为 I/O 绑定,请使用 async 和 await(而不使用 Task.Run)。 不应使用任务并行库。

如果你的工作属于 CPU 绑定,并且你重视响应能力,请使用 async 和 await,但在另一个线程上使用 Task.Run 生成工作。 如果该工作同时适用于并发和并行,还应考虑使用任务并行库。

此外,应始终对代码的执行进行测量。 例如,你可能会遇到这样的情况:多线程处理时,上下文切换的开销高于 CPU 绑定工作的开销。 每种选择都有折衷,应根据自身情况选择正确的折衷方案。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!优秀是一种习惯,欢迎大家留言学习!


http://www.mrgr.cn/news/28139.html

相关文章:

  • Java poi 模板导出Word 带图片
  • 【Java Web】Servlet
  • 运维工具Ansible部署、配置
  • 【已解决】git push一直提示输入用户名及密码、fatal: Could not read from remote repository的问题
  • 【阅读记录-章节1】Build a Large Language Model (From Scratch)
  • 如何查看本地的个人SSH密钥
  • 鸿蒙开发之ArkTS 基础五 箭头函数
  • 信息安全工程师(5)域名与域名解析
  • 力扣周赛:第415场周赛
  • python的数据类型详解
  • Java企业面试题3
  • 基于是springboot小区物业管理系统
  • JAVA基础:线程优先级和精灵线程
  • 【演化博弈论】:双方演化博弈的原理与过程
  • java数据结构----图
  • 【Kubernetes】常见面试题汇总(十九)
  • C++第五十一弹---IO流实战:高效文件读写与格式化输出
  • 数据结构之基数排序简介与举例
  • 图像增强技术分析
  • aspcms webshell漏洞复现
  • 卡拉兹(Callatz)猜想也叫(3n+1)猜想
  • 【数据结构】排序算法---希尔排序
  • 第T11周:优化器对比实验
  • Vue基础
  • 【C++】深入理解作用域和命名空间:从基础到进阶详解
  • 深入浅出Java匿名内部类:用法详解与实例演示