广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >c# 异步编程基础讲解
  • 480
分享到

c# 异步编程基础讲解

2024-04-02 19:04:59 480人浏览 薄情痞子
摘要

目录Task 和 Task<T>I/O 受限异步操作CPU 受限异步操作异步编程模式现代应用程序广泛使用文件和网络 I/O。I/O 相关 api 传统上默认是阻塞的,导致

现代应用程序广泛使用文件和网络 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(如 linuxSocket 的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# 异步编程的资料请关注编程网其它相关文章!

--结束END--

本文标题: c# 异步编程基础讲解

本文链接: https://www.lsjlt.com/news/123257.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • c# 异步编程基础讲解
    目录Task 和 Task<T>I/O 受限异步操作CPU 受限异步操作异步编程模式现代应用程序广泛使用文件和网络 I/O。I/O 相关 API 传统上默认是阻塞的,导致...
    99+
    2022-11-12
  • C# 异步多线程入门基础
    目录进程、线程1. 进程 2. 线程 分时、分片同步、异步异步、多线程异步多线程效率多线程无序性扩展异步多线程版本下一篇:C# 异步多线程入门到精通之Thread篇 进程、线程 1....
    99+
    2022-11-12
  • Java零基础讲解异常
    目录什么是异常?异常的处理异常的抛出处理异常throws声明异常捕获异常finally:异常的处理流程自定义异常什么是异常? 异常在我们写代码是特别常见,因为程序员大部分时间都在修复...
    99+
    2022-11-13
  • rust异步编程详细讲解
    目录简化版本 Future理解Future的模型async/awit 使用简化版本 Future // wake 函数 reactor发现状态是ready 通知executor 函数...
    99+
    2022-12-16
    rust异步编程 rust异步原理
  • C#枚举类型的基础讲解
    本篇内容主要讲解“C#枚举类型的基础讲解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C#枚举类型的基础讲解”吧!对于C#枚举类型不仅可以提高程序的可读性,而且可以减少因底层值发生改变而导致的程...
    99+
    2023-06-18
  • C#表达式树Expression基础讲解
    什么是表达式树 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等。可以对表达式树中的代码进行编辑和运算。 这样能够动态...
    99+
    2022-11-12
  • c语言 指针零基础讲解
    1.指针是什么(可能有点难理解) 指针的是啥? 指针实际上就是地址,地址就是系统给定的编号,编号就是一个个内存单元。 在某种情况来说指针=地址=编号=内存单元。 指针就是地址,顾名思...
    99+
    2022-11-13
  • C++入门之模板基础讲解
    目录前言引入模板函数模板模板的匹配原则模板的显示调用类模板注意1注意2总结 前言 今天博主将要介绍的内容是–模板,他在C++中具有非常重要的位置.至于什么是模板呢?我们请看...
    99+
    2022-11-12
  • C语言程序的编译与预处理基础定义讲解
    目录程序的翻译环境和执行环境1.翻译环境2.运行环境预处理详解预定义符号#define#define定义宏#define替换规则#和##带副作用的宏参数宏和函数对比命名约定#unde...
    99+
    2022-11-13
  • C/C++网络编程基础知识超详细讲解上部分(系统性学习day11)
    目录 前言 一、网络的含义与构成 含义: 构成:  二、网络的体系结构 1>OSI七层模型 2>TCP/IP协议体系结构  3>数据经过体系结构,怎么封装  4>端口号 5>大小端序 6>TCP/UDP传输层的协议  三、系统函数API学...
    99+
    2023-10-21
    网络 学习 c语言 c# 开发语言
  • C#异步编程之async/await详解
    目录概述C#异步编程用法async/await和Task简介asyncawaitTask其他实现原理剖析实现原理示例概述 异步这个概念在不同语境下有不同的解释,比如在一个单核CPU里...
    99+
    2023-03-11
    C#异步编程async await C#异步编程 C# async await
  • C语言零基础讲解指针和数组
    目录一、指针和数组分析-上1.数组的本质2.指针的运算3.指针的比较4.小结二、指针与数组分析-下 1.数组的访问方式2.下标形式 VS 指针形式3.a 和 &a ...
    99+
    2022-11-13
  • C++基础入门篇之强制转换讲解
    本篇内容主要讲解“C++基础入门篇之强制转换讲解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++基础入门篇之强制转换讲解”吧!引言假设有基类 A,包含了虚函数 func1,以及有派生类 B,...
    99+
    2023-06-07
  • c# 异步编程入门
    目录一、什么算异步? 二、在编程中的异步 三、原始的异步编程模式之回调函数# 1、回调函数 一、什么算异步?   广义来讲,两个工作流能同时进行就算异步,例...
    99+
    2022-11-12
  • C#实现基于任务的异步编程模式
    目录一.延续任务二.同步上下文三.使用多个异步方法1.按顺序调用异步方法2.使用组合器四.转换异步模式五.错误处理1.异步方法的异常处理2.多个异步方法的异常处理3.使用Aggreg...
    99+
    2022-11-13
  • C#异步编程async/await用法详解
    异步函数简介 一般指 async 修饰符声明得、可包含await表达式得方法或匿名函数。 声明方式 异步方法的声明语法与其他方法完全一样, 只是需要包含 async 关键字。asyn...
    99+
    2022-11-13
  • C#图形编程GDI+基础介绍
    编写图形程序时需要使用GDI(Graphics Device Interface,图形设备接口),从程序设计的角度看,GDI包括两部分:一部分是GDI对象,另一部...
    99+
    2022-11-13
  • Python基础学习教程系列讲解——try_except异常处理机制
    在Python编程中不可避免的会出现错误,在调试阶段出现语法之类的错误时,Pycharm会在Debug窗口提示错误,但是程序在运行时由于内部隐含的问题而引起错误,会导致程序终止执行。比如以下例程中,使用urllib库打开URL时由于网络问题...
    99+
    2023-06-02
  • Linux系统中bash shell编程的10个基础问题讲解
    第1问:为何叫做shell? 在介绍 shell 是什么东西之前,不妨让我们重新审视使用者与电脑的关系。我们知道电脑的运作不能离开硬件,但使用者却无法直接对硬件作驱动,硬件的驱动只能透过一个称为“操作系统(...
    99+
    2022-06-04
    基础 系统 Linux
  • C语言从基础到进阶全面讲解数组
    目录1.基础知识2.数组的分类2.1按元素类型分类2.2按维数分类3.数组定义和初始化3.1 一维数组3.2 二维数组4.数组元素的引用方法5.字符数组的定义1.基础知识 C语言中使...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作