C#多线程(5)——异步方法async与await

在上一章节中,为大家介绍了C#多线程(4)——任务并行库TPL,TPL是从.NetFramwork4.0后引入的基于异步操作的一组API,核心关注于任务【 T a s k 和 T a s k < T > \textcolor{red}{Task 和 Task<T>} TaskTask<T>】。简化了我们异步编程的步骤,但在C#5.0时,引入了新的语言特性—— 异步方法 \textcolor{red}{异步方法} 异步方法,是一种语法糖,是TPL之上的更高级别的抽象,它遵循了基于任务的异步模式,仅仅只使用了async与await关键字,更加简化了异步编程,可以避免性能瓶颈并增强应用程序的总体响应能力。

1 异步方法

  • 异步方法是用 a s y n c \textcolor{blue}{async} async关键字修饰的方法,通常方法的名称会以Async关键字结尾。官方提供了大量的异步方法。
  • 方法的返回值有两种 【 T a s k ,方法体中无 r e t u r n 语句和 T a s k < T > 方法体中 r e t u r n T \textcolor{red}{Task ,方法体中无return语句 和 Task<T> 方法体中return T } Task,方法体中无return语句和Task<T>方法体中returnT】,这个与TPL模型的任务模型是一致的,都是对异步逻辑的封装
  • 异步方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。
  • .NET Framework 4.5 或更高版本以及 .NET Core 包含许多异步方法。 例如,System.IO.Stream 类包含 CopyToAsync、ReadAsync 和 WriteAsync 等方法,以及同步方法 CopyTo、Read 和 Write。
 public static async Task<int> GetUrlContentLengthAsync()
 {
            HttpClient client = new HttpClient();
            Task<string> getStringTask = client.GetStringAsync("https://learn.microsoft.com/dotnet");
            Console.WriteLine("GetUrlContentLengthAsync 方法中的的Independent work"); //执行不依赖于 GetStringAsync 得出的最终结果的其他工作
            string contents = await getStringTask;
            return contents.Length; //返回值被包装为Task<string>类型
}

2 async、await原理

使用ILSpy对.dll文件进行进行反编译知,async await是语法糖,底层是 状态机 \textcolor{red} {状态机} 状态机的调用。async标注的方法会被反编译成一个类,会根据await的调用被切分为多个状态,
对async方法的调用会被拆分成MoveNext的调用(根据状态多次进入switch判断体)

在这里插入图片描述

3 异步方法与多线程的关系

异步方法旨在成为非阻止操作,即不阻塞异步方法调用方的线程。 \textcolor{red}{异步方法旨在成为非阻止操作,即不阻塞异步方法调用方的线程。} 异步方法旨在成为非阻止操作,即不阻塞异步方法调用方的线程。

await调用的等待期间,.Net会把当前的线程返回给线程池,等异步方法调用执行完成后,框架会从线程池中再取一个线程执行后续的代码(可能与前一个线程相同,也有可能是一个新的线程,这取决于CPU对线程池中线程的调度。await关键字前后可能是不一样的线程)。
线程的切换,主要取决于异步方法中的任务类型及实际CLR对线程的调度:
- I O 密集型 \textcolor{blue}{IO密集型} IO密集型:大部分时间都是cpu在等IO的读写操作。如网络请求数据、访问数据库或读取和写入到文件系统
- C P U 密集型 \textcolor{blue}{CPU密集型} CPU密集型:例如执行成本高昂【耗时长,占用大量内存空间】的计算,

测试 I O 密集型任务, M a i n 方法中关键代码块如下。 \textcolor{red}{测试IO密集型任务, Main方法中关键代码块如下。} 测试IO密集型任务,Main方法中关键代码块如下。

Console.OutputEncoding = Encoding.UTF8;

            string des = "E:\\logs\\2.txt";
            //定义一个异步方法,从指定的url下载html内容后再写入到本地文件夹中。
            async Task DownloadFromUrlAsycn(string filename)
            {
                using (HttpClient client = new HttpClient())
                {

                    Console.WriteLine("下载前 ,ThreadId is {0},是否为线程池线程{1}", Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.IsThreadPoolThread);
                    //获得url的页面资源 (网络传输,属于IO密集型的任务,此时CPU空闲,为了充分的利用CPU,可能会出现CPU上下文的切换,await前后发现线程的切换)
                    string s = await client.GetStringAsync("https://learn.microsoft.com/dotnet");
                    Console.WriteLine("下载后 ,ThreadId is {0},是否为线程池线程{1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);

                    Console.WriteLine("写入文件前 ,ThreadId is {0},是否为线程池线程{1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
                    //写入执行资源文件
                    await File.WriteAllTextAsync(filename, s);
                    Console.WriteLine("写入文件后 ,ThreadId is {0},是否为线程池线程{1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);

                }
            }

            Console.WriteLine("方法前,ThreadId is {0},是否为线程池线程{1} ", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
            await DownloadFromUrlAsycn(des);
            Console.WriteLine("方法后,ThreadId is {0},是否为线程池线程{1} ", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);

            Console.ReadKey();

运行Main方法后,得到下图结果

在这里插入图片描述

可以发现,切换线程主要是发生在网络传输和文件写入的前后【IO读写动作】,IO的读写是不需要使用CPU的,只需要发一个命令给硬盘即可,硬盘处理完后会再通知cpu继续处理。为了充分利用CPU在IO读写的阶段,通常会将线程返回到线程池中。该线程可以用于其他的CPU操作。

测试 C P U 密集型任务, M a i n 方法中关键代码块如下。 \textcolor{red}{测试CPU密集型任务, Main方法中关键代码块如下。} 测试CPU密集型任务,Main方法中关键代码块如下。

     Console.OutputEncoding = Encoding.UTF8;
     Console.WriteLine("方法前线程id为{0}", Thread.CurrentThread.ManagedThreadId);
     async Task GetStringAsync() {
                Console.WriteLine("方法中线程id为{0}",Thread.CurrentThread.ManagedThreadId);
                for (int i = 0; i < 5; i++) {
                    Thread.Sleep(TimeSpan.FromSeconds(1)); //模拟CPU计算需要耗时
                };
     };

    await GetStringAsync();
    Console.WriteLine("方法后线程id为{0}", Thread.CurrentThread.ManagedThreadId);

    Console.WriteLine("");

    Task t=  Task.Run(()=>GetStringAsync()) ;
    t.Wait();
   Console.WriteLine(" Task.Run方法后线程id为{0}",    Thread.CurrentThread.ManagedThreadId); 
   //对于CPU密集型的异步方法时,通常会等待一个使用 Task.Run 方法在后台线程启动的操作。

在这里插入图片描述

(1)当是一个CPU密集型的异步方法时,线程的切换涉及到很多资源的消耗,在这种情况下,使用新的线程去执行异步方法的逻辑并不会优于同步执行的逻辑。所以await GetStringAsync()的前后都是使用同一个线程。
(2)综上所述,根据代码的验证我们可以知道异步方法并不能等价于多线程,调用异步方法时,并不是都会通过新的线程去执行异步逻辑,在没有线程切换的状态下【通常是CPU密集型的任务】,与同步方法是一致的。为了达到异步的效果,我们可以使用Task.Run()的方法,在后台线程中执行异步操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/454826.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS NEXT应用开发之下拉刷新与上滑加载案例

介绍 本示例介绍使用第三方库的PullToRefresh组件实现列表的下拉刷新数据和上滑加载后续数据。 效果图预览 使用说明 进入页面&#xff0c;下拉列表触发刷新数据事件&#xff0c;等待数据刷新完成。上滑列表到底部&#xff0c;触发加载更多数据事件&#xff0c;等待数据加载…

基于Springboot的集团门户网站(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的集团门户网站&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

YOLOv5目标检测学习(5):源码解析之:推理部分dectet.py

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、导入相关包与路径、模块配置1.1 导入相关的python包1.2 获取当前文件的相对路径1.3 加载自定义模块1.4 总结 二、执行主体的main函数所以执行推理代码&…

手写简易操作系统(六)--内存分页

前情提要 上一节我们讲到了获取物理内存&#xff0c;这节我们将开启内存分页 一、内存分页的作用 内存分页是一种操作系统和硬件协同工作的机制&#xff0c;用于将物理内存分割成固定大小的页面&#xff08;通常为4KB&#xff09;并将虚拟内存空间映射到这些页面上。内存分页…

Django官网项目 五

Writing your first Django app, part 5 | Django documentation | Django 自动测试介绍 何为自动测试 测试有系统自动完成。你只需要一次性的编写测试代码&#xff0c;当程序代码变更后&#xff0c;不需要对原来的测试人工再重新测试一遍。系统可以自动运行原来编写的测试代…

【unity资源加载与优化章】Profiler优化工具详解

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

今日AI:GPT-4.5意外曝光可能6月发布、UP主借AI识别情绪播放量186万、全球首个AI程序员诞生

欢迎来到【今日AI】栏目!这里是你每天探索人工智能世界的指南&#xff0c;每天我们为你呈现AI领域的热点内容&#xff0c;聚焦开发者&#xff0c;助你洞悉技术趋势、了解创新AI产品应用。 新鲜AI产品点击了解:AIbase - 智能匹配最适合您的AI产品和网站 &#x1f4e2;一分钟速…

简述类与对象

一、两者关系 类是Java语言中最重要的数据类型&#xff0c;用于创建具体实例&#xff08;对象&#xff09; 抽象出一类事物共有的属性和行为&#xff0c;即数据以及数据上的操作 类是对现实事物的模拟&#xff0c;包含属性&#xff08;成员变量&#xff09;和行为&#xff0…

《如何使用C语言去下三子棋?》

目录 一、环境配置 二、功能模块 1.打印菜单 2.初始化并打印棋盘 3、行棋 3.1玩家行棋 3.2电脑行棋 4、判断是否和棋 5.判赢 三、代码实现 1、test.c文件 2、game.c文件 3、game.h文件 一、环境配置 本游戏用到三个文件&#xff0c;分别是两个源文件test.c game.c 和…

排序算法之快速排序算法介绍

目录 快速排序介绍 时间复杂度和稳定性 代码实现 C语言实现 c实现 java实现 快速排序介绍 快速排序(Quick Sort)使用分治法策略。 它的基本思想是&#xff1a;选择一个基准数&#xff0c;通过一趟排序将要排序的数据分割成独立的两部分&#xff1b;其中一部分的所有数据…

动态规划——传球问题

题目链接&#xff1a;1.传球游戏 - 蓝桥云课 (lanqiao.cn) 本题关键在于动态规划的数组设计&#xff0c;以及围坐一圈时索引的变化。 首先是动态规划&#xff0c;由于是求球传递m次回到第一位同学&#xff0c;那么就可以设计成一个二维数组&#xff0c;每个位置代表的是&#x…

【LeetCode热题100】240. 搜索二维矩阵 II

一.题目要求 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。 ‘每列的元素从上到下升序排列。 二.题目难度 中等 三.输入样例 示例 1&#xff1a; 输入&#xff1a;matrix [[1,4,7…

蓝桥杯 填空 卡片

蓝桥杯 填空题 卡片 解题思路&#xff1a; 我们只需要消耗完卡片的个数即可。 代码示例&#xff1a; #include<bits/stdc.h> using namespace std; int a[10]; bool isEnd(){for(int i0;i<10;i){if(a[i]-1)return false;}return true; } bool getN(int x){while(x){i…

ARM 汇编指令:(五)CMP指令

目录 1.CMP比较指令 2.指令条件码 cond 1.CMP比较指令 CMP指令是计算机指令集中的一种比较指令&#xff0c;用于比较两个操作数的大小关系或相等性&#xff0c;并根据比较结果设置或更新条件码寄存器&#xff08;或程序状态字&#xff09;的标志位。 指令格式&#xff1a;C…

jenkins + gitea 自动化部署Docker项目(vue + .NET Core)

废话不多说&#xff0c;服务先安装好Jenkins 和 gitea 理论上 gitlab 一样的实现流程 Jenkins 配置&#xff1a; 第一步装插件 安装 Generic Event 安装 gitea 相关插件 创建一个任务 设置 git 根据自己git 的认证填写对应的认证方式 构建环境记得勾选这个&#xff0c;会清…

【关注】国内外经典大模型(ChatGPT、LLaMA、Gemini、DALL·E、Midjourney、文心一言、千问等

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

Python XML数据处理库之xmltodict使用详解

概要 在 Python 的开发中,处理 XML 数据是一项常见的任务。然而,Python 标准库中的 XML 解析器使用起来可能较为繁琐,需要编写大量的代码来处理 XML 数据。幸运的是,有一个名为 xmltodict 的第三方库可以帮助我们简化这个过程。本文将深入探讨 xmltodict 库的各个方面,包…

16、设计模式之观察者模式(Observer)

一、什么是观察者模式 观察者模式属于行为型模式。在程序设计中&#xff0c;观察者模式通常由两个对象组成&#xff1a;观察者和被观察者。当被观察者状态发生改变时&#xff0c;它会通知所有的观察者对象&#xff0c;使他们能够及时做出响应&#xff0c;所以也被称作“发布-订…

【git】GitHub仓库没有 Contribution activity

解决方案 检查并更改本地的 git 绑定的邮箱和名字 git config --global user.name "Your New Name" git config --global user.email "yournewemailexample.com"查询方式 git config --global user.name git config --global user.email成功显示

opencv dnn模块 示例(25) 目标检测 object_detection 之 yolov9

文章目录 1、YOLOv9 介绍2、测试2.1、官方Python测试2.1.1、正确的脚本2.2、Opencv dnn测试2.2.1、导出onnx模型2.2.2、c测试代码 2.3、测试统计 3、自定义数据及训练3.1、准备工作3.2、训练3.3、模型重参数化 1、YOLOv9 介绍 YOLOv9 是 YOLOv7 研究团队推出的最新目标检测网络…