我的编程空间,编程开发者的网络收藏夹
学习永远不晚

c# 异步编程基础讲解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

c# 异步编程基础讲解

现代应用程序广泛使用文件和网络 I/O。I/O 相关 API 传统上默认是阻塞的,导致用户体验和硬件利用率不佳,此类问题的学习和编码的难度也较大。而今基于 Task 的异步 API 和语言级异步编程模式颠覆了传统模式,使得异步编程非常简单,几乎没有新的概念需要学习。

异步代码有如下特点:

  • 在等待 I/O 请求返回的过程中,通过让出线程来处理更多的服务器请求。
  • 通过在等待 I/O 请求时让出线程进行 UI 交互,并将长期运行的工作过渡到其他 CPU,使用户界面的响应性更强。
  • 许多较新的 .NET API 都是异步的。
  • 在 .NET 中编写异步代码很容易。

使用 .NET 基于 Task 的异步模型可以直接编写 I/O 和 CPU 受限的异步代码。该模型围绕着Task和Task<T>类型以及 C# 的async和await关键字展开。本文将讲解如何使用 .NET 异步编程及一些相关基础知识。

Task 和 Task<T>

Task 是 Promise 模型的实现。简单说,它给出“承诺”:会在稍后完成工作。而 .NET 的 Task 是为了简化使用“承诺”而设计的 API。

Task 表示不返回值的操作, Task<T> 表示返回T类型的值的操作。

重要的是要把 Task 理解为发起异步工作的抽象,而不是对线程的抽象。默认情况下,Task 在当前线程上执行,并酌情将工作委托给操作系统。可以选择通过Task.RunAPI 明确要求任务在单独的线程上运行。

Task 提供了一个 API 协议,用于监视、等待和访问任务的结果值。比如,通过await关键字等待任务执行完成,为使用 Task 提供了更高层次的抽象。

使用 await 允许你在任务运行期间执行其它有用的工作,将控制权交给其调用者,直到任务完成。你不再需要依赖回调或事件来在任务完成后继续执行后续工作。

I/O 受限异步操作

下面示例代码演示了一个典型的异步 I/O 调用操作:


public Task<string> GetHtmlAsync()
{
    // 此处是同步执行
    var client = new HttpClient();
    return client.GetStringAsync("https://www.dotnetfoundation.org");
}

这个例子调用了一个异步方法,并返回了一个活动的 Task,它很可能还没有完成。

下面第二个代码示例增加了async和await关键字对任务进行操作:


public async Task<string> GetFirstCharactersCountAsync(string url, int count)
{
    // 此处是同步执行
    var client = new HttpClient();

    // 此处 await 挂起代码的执行,把控制权交出去(线程可以去做别的事情)
    var page = await client.GetStringAsync("https://www.dotnetfoundation.org");

    // 任务完成后恢复了控制权,继续执行后续代码
    // 此处回到了同步执行

    if (count > page.Length)
    {
        return page;
    }
    else
    {
        return page.Substring(0, count);
    }
}

使用 await 关键字告诉当前上下文赶紧生成快照并交出控制权,异步任务执行完成后会带着返回值去线程池排队等待可用线程,等到可用线程后,恢复上下文,线程继续执行后续代码。

GetStringAsync() 方法的内部通过底层 .NET 库调用资源(也许会调用其他异步方法),一直到 P/Invoke 互操作调用本地(Native)网络库。本地库随后可能会调用到一个系统 API(如 Linux 上 Socket 的write()API)。Task 对象将通过层层传递,最终返回给初始调用者。

在整个过程中,关键的一点是,没有一个线程是专门用来处理任务的。虽然工作是在某种上下文中执行的(操作系统确实要把数据传递给设备驱动程序并中断响应),但没有线程专门用来等待请求的数据回返回。这使得系统可以处理更大的工作量,而不是干等着某个 I/O 调用完成。

虽然上面的工作看似很多,但与实际 I/O 工作所需的时间相比,简直微不足道。用一条不太精确的时间线来表示,大概是这样的:

0-1--------------------2-3

从0到1所花费的时间是await交出控制权之前所花的时间。从1到2花费的时间是GetStringAsync方法花费在 I/O 上的时间,没有 CPU 成本。最后,从2到3花费的时间是上下文重新获取控制权后继续执行的时间。

CPU 受限异步操作

CPU 受限的异步代码与 I/O 受限的异步代码有些不同。因为工作是在 CPU 上完成的,所以没有办法绕开专门的线程来进行计算。使用 async 和 await 只是为你提供了一种干净的方式来与后台线程进行交互。请注意,这并不能为共享数据提供加锁保护,如果你正在使用共享数据,仍然需要使用适当的同步策略。

下面是一个 CPU 受限的异步调用:


public async Task<int> CalculateResult(InputData data)
{
    // 在线程池排队获取线程来处理任务
    var expensiveResultTask = Task.Run(() => DoExpensiveCalculation(data));

    // 此时此处,你可以并行地处理其它工作

    var result = await expensiveResultTask;

    return result;
}

CalculateResult方法在它被调用的线程(一般可以定义为主线程)上执行。当它调用Task.Run时,会在线程池上排队执行 CPU 受限操作 DoExpensiveCalculation,并接收一个Task<int>句柄。DoExpensiveCalculation会在下一个可用的线程上并行运行,很可能是在另一个 CPU 核上。和 I/O 受限异步调用一样,一旦遇到await,CalculateResult的控制权就会被交给它的调用者,这样在DoExpensiveCalculation返回结果的时候,结果就会被安排在主线程上排队运行。

对于开发者,CUP 受限和 I/O 受限的在调用方式上没什么区别。区别在于所调用资源性质的不同,不必关心底层对不同资源的调用的具体逻辑。编写代码需要考虑的是,对于 CUP 受限的异步任务,根据实际情况考虑是否需要使其和其它任务并行执行,以加快程序的整体运行时间。

异步编程模式

最后简单回顾一下 .NET 历史上提供的三种执行异步操作的模式。

  • 基于任务的异步模式(Task-based Asynchronous Pattern,TAP),它使用单一的方法来表示异步操作的启动和完成。TAP 是在 .NET Framework 4 中引入的。它是 .NET 中异步编程的推荐方法。C# 中的 async 和 await 关键字为 TAP 添加了语言支持。
  • 基于事件的异步模式(Event-based Asynchronous Pattern,EAP),这是基于事件的传统模式,用于提供异步行为。它需要一个具有 Async 后缀的方法和一个或多个事件。EAP 是在 .NET Framework 2.0 中引入的。它不再被推荐用于新的开发。
  • 异步编程模式(Asynchronous Programming Model,APM)模式,也称为 IAsyncResult 模式,这是使用 IAsyncResult 接口提供异步行为的传统模式。在这种模式中,需要Begin和End方法同步操作(例如,BeginWrite和EndWrite来实现异步写操作)。这种模式也不再推荐用于新的开发。

下面简单举例对三种模式进行比较。

假设有一个 Read 方法,该方法从指定的偏移量开始将指定数量的数据读入提供的缓冲区:


public class MyClass
{
    public int Read(byte [] buffer, int offset, int count);
}

若用 TAP 异步模式来改写,该方法将是简单的一个 ReadAsync 方法:


public class MyClass
{
    public Task<int> ReadAsync(byte [] buffer, int offset, int count);
}

若使用 EAP 异步模式,需要额外多定义一些类型和成员:


public class MyClass
{
    public void ReadAsync(byte [] buffer, int offset, int count);
    public event ReadCompletedEventHandler ReadCompleted;
}

public delegate void ReadCompletedEventHandler(
    object sender, ReadCompletedEventArgs e);

public class ReadCompletedEventArgs : AsyncCompletedEventArgs
{
    public MyReturnType Result { get; }
}

若使用 AMP 异步模式,则需要定义两个方法,一个用于开始执行异步操作,一个用于接收异步操作结果:


public class MyClass
{
    public IAsyncResult BeginRead(
        byte [] buffer, int offset, int count,
        AsyncCallback callback, object state);
    public int EndRead(IAsyncResult asyncResult);
}

后两种异步模式已经过时不推荐使用了,这里也不再继续探讨。岁数大点的 .NET 程序员可能比较熟悉后两种异步模式,毕竟那时候没有 async/await,应该没少折腾。

以上就是c# 异步编程基础讲解的详细内容,更多关于c# 异步编程的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

c# 异步编程基础讲解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

rust异步编程详细讲解

这篇文章主要介绍了rust异步编程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
2022-12-16

Node.js 基础:从头开始掌握异步编程

掌握异步编程是 Node.js 开发的基础。本文将从头开始介绍 Node.js 的异步编程模型,包括事件循环、回调函数和 Promise 等核心概念,帮助您构建高效和响应迅速的 Node.js 应用程序。
Node.js 基础:从头开始掌握异步编程
2024-03-09

C#异步编程之async/await详解

异步这个概念在不同语境下有不同的解释,不同的编程语言有不同异步编程方法,在C#语言中,常常使用async/await等关键字,和Task等类来实现异步编程。本文就来和大家聊聊async与await吧
2023-03-11

C#枚举类型的基础讲解

本篇内容主要讲解“C#枚举类型的基础讲解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C#枚举类型的基础讲解”吧!对于C#枚举类型不仅可以提高程序的可读性,而且可以减少因底层值发生改变而导致的程
2023-06-18

JavaScript编程基础:一步步掌握JavaScript精髓

JavaScript编程基础入门指南,帮助您一步一步掌握JavaScript的精髓,从语法基础到实际应用,全方位覆盖。JavaScript、编程、基础、语法、函数、对象、事件、浏览器、应用
JavaScript编程基础:一步步掌握JavaScript精髓
2024-02-09

Python基础学习教程系列讲解——try_except异常处理机制

在Python编程中不可避免的会出现错误,在调试阶段出现语法之类的错误时,Pycharm会在Debug窗口提示错误,但是程序在运行时由于内部隐含的问题而引起错误,会导致程序终止执行。比如以下例程中,使用urllib库打开URL时由于网络问题
2023-06-02

C#异步编程的示例分析

小编给大家分享一下C#异步编程的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!异步编程在处理并发方面被使用的越来越多,之所以说上面一句话,是为了区分多线程
2023-06-17

C#BeginInvoke实现异步编程方式

这篇文章主要介绍了C#BeginInvoke实现异步编程方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-01-28

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录