c#入门-异步方法

news2024/11/15 4:48:45

异步方法

如果一个操作会返回Task,那么用这个操作续接后续操作,也会得到Task。
也就是说Task具有传染性,最终拼凑出来的Task非常复杂。
使用异步方法,可以简化Task的拼凑。

async修饰

异步方法需要添加async修饰符。并且通常方法名以Async结尾。
在异步方法内可以使用await关键字
(因为以前它不是关键字,可能有人用这个作为变量名。
为了不破坏以前的代码,只有在有async修饰是await才是关键字)。

返回值

返回值必须是void或Task或Task的泛型版本。
异步方法内直接返回值,编译器会自动把他封装成Task。
例如方法返回值是Task<int>,那你只要返回int就行了。

await

await可以放置在一个Task的前面。这回等待它完成,并获取他的返回值。

async Task<string[]> ModAsync(string path, string url)
{
	Task<string[]> file = File.ReadAllLinesAsync(path);
	Task<string[]> http = File.ReadAllLinesAsync(url);//假设这是个网络访问

	var txt = await await Task.WhenAny(file, http);
	var taskArr = new Task<string>[txt.Length];
	for (int i = 0; i < taskArr.Length; i++)
	{
		taskArr[i] = File.ReadAllTextAsync(txt[i]);
	}
	return await Task.WhenAll(taskArr);
}

和直接调用Result或Wait不同的是,await会立刻视为方法完成然后继续运行这个方法后面的内容。

Console.WriteLine("主线程开始,即将调用异步方法");
var task = DelayAsync();
Console.WriteLine("异步方法调用完毕,等待异步方法输出值");
Console.WriteLine(task.Result);

async Task<int> DelayAsync()
{
	Console.WriteLine("异步方法开始执行,等待1秒钟");
	await Task.Delay(1000);
	Console.WriteLine("异步方法执行结束");
	return 12;
}
主线程开始,即将调用异步方法
异步方法开始执行,等待1秒钟
异步方法调用完毕,等待异步方法输出值
异步方法执行结束
12

可以看到,异步方法还没有结束的时候,主线程已经运作到了调用完毕处。
而await后面没有结束的内容,就会被打包成一个Task。在后台默默继续运行。
所以如果一个异步方法没有await的内容,就会弹出一个警告
在这里插入图片描述

状态机

迭代器和异步方法都会把方法内容做成一个很复杂的东西。
迭代器执行到yield return时就会停止,直到下一次被调用。
异步方法执行到await就会挂起线程,当等待结束时重新排队线程。

但他们把内容做复杂的时候,把变量的作用域还原的很好。
使用using语句的时候,一旦出了作用域,就会执行释放方法。

Task<string> Get4399()
{
	using HttpClient client = new HttpClient();
	return client.GetStringAsync("www.4399.com");
}

async Task<string> Get4399Async()
{
	using HttpClient client = new HttpClient();
	return await client.GetStringAsync("www.4399.com");
}

第一个方法的网络访问会直接得到一个Task。把它直接返回是能和返回类型对应上的。
但实际上这样会报错。因为这个方法已经结束了,网络类就会关闭连接。
而还没有执行完成的任务,是需要保持连接才能访问到内容的。所以这个任务无法继续。
而第二个方法,会等到数据获取完成,这个方法才真正结束,网络类才释放。

即刻完成的任务

有时候,为了返回值能和接口对应上,或者重写基类的异步方法,
需要返回值是Task。而自己实际方法体内不需要等待。
又或者在异步方法内需要象征性的去await一个东西。
此时没有必要去创建一个需要通过线程池执行的任务,可以创建一个直接完成的任务。

已完成任务

CompletedTask静态属性是一个已经完成的任务。可以用来作为无返回值的任务来返回。

Task DoSomeAsync()
{
	return Task.CompletedTask;
}

从值创建完成任务

FromResult静态方法可以创建一个任务,并把他的输出设置为指定的值。

Task<int> GetIntAsync()
{
	return Task.FromResult(12);
}

屈服

Yield方法会创建一个可以await的东西,但不是Task。
它会让这个线程重新排队,等待一段非常小的时间。

async Task<int> DelayAsync()
{
	Console.WriteLine("异步方法开始执行,等待1秒钟");
	await Task.Yield();
	Console.WriteLine("异步方法执行结束");
	return 12;
}

如果使用Task.Delay(0)是达不到这样的效果的。
主线程不是每次遇到await都会视为方法结束,而是在需要等待await完成的时候才会结束。
把0作为延迟的参数,创建出来的就是一个即刻完成的任务。

而使用Task.Delay(1)也达不到这样的效果。
尽管你指定只延迟这么短暂的一点时间。但实际上操作系统无法管理得这么细致。
实际效果可能跟Task.Delay(15)差不多。

切换上下文

在窗体应用中(WPF,winfrom,unity),通常主线程会进行死循环操作,
为你刷新界面,这称为UI线程。为了线程安全,所有改变UI的操作只允许在UI线程中进行

通常,这类程序会在启动时配置线程环境(参阅SynchronizationContext),
让你的异步代码在await等待结束后,在合理时机,暂停UI线程的工作,让他接手await之后的操作。
因此,在窗体应用中使用异步方法,可以在方法内操控UI。

但如果不是使用await等待任务,而是调用了Wait或Result,会导致死锁
(你在控制台程序无法复现这个场景,因为控制台是系统调用的窗体,不是你程序的一部分)。

因为异步方法里的东西没有执行完成,他们需要继续执行才能完成。
而你使用Wait进行等待,主线程就会啥也不干。那他就干不了异步方法里的后续操作。
而后续操作没完成,主线程就得继续等。

Task的ConfigureAwait方法可以为当前的任务改变配置,让他可以通过别的线程来接手。
这样可以避免调用Wait造成的死锁。

async Task<int> Sum()
{
	return await Task.Run(() =>
	{
		int sum = 0;
		for (int i = 0; i < 100; i++)
		{
			sum += i;
		}
		return sum;
	}).ConfigureAwait(false);
}

在第一个await的任务后面加上ConfigureAwait(false),会告诉线程池让别的线程来接手。
ConfigureAwait(true)是默认行为,不需要调用。调用了也只是尽量让同一线程接手,不能保证必然。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【前端】Vue项目:旅游App-(15)home:网络请求house数据、动态并组件化展示house列表信息

文章目录目标过程与代码content组件请求数据&#xff1a;houseListrequeststore控制台输出动态加载更多列表数据house-item组件阶段1&#xff1a;数据传送阶段2&#xff1a;对着目标写样式house-item-v9house-item-v9&#xff1a;debughouse-item-v3阶段3&#xff1a;总体效果效…

Android ANR触发机制(二)

上一篇文章看了Service的ANR触发流程&#xff0c;现在看一下其他三种ANR触发流程。 1.BroadcastReceiver触发ANR BroadcastReceiver超时是位于ActivityManager线程中的BroadcastQueue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息时触发。 广播队列分为foreground队列和b…

基于Java+SpringBoot+Vue前后端分离学生管理系统设计与实现

博主介绍&#xff1a;✌全网粉丝3W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战✌ 博主作品&#xff1a;《微服务实战》专栏是本人的实战经验总结&#xff0c;《Spring家族及…

2023年二月份图形化二级打卡试题

活动时间 从2023年 2月1日至1月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; &#xff08;1&#xff09;小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 &#xff08;2&#xff09;小朋友做完题目后&…

数组中和为0的三个数

给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意: 答案中不可以包含重复的三元组。 示例 1: 输入: num…

了解SLI、SLO和SLA

了解SLI、SLO和SLA 概念解释 服务水平指标(SLI) SLI代表目前服务的状态&#xff0c;例如可以是最基本的接口成功率、p99响应时间&#xff0c;也可以是一些业务指标&#xff0c;例如用户投诉率之类的。是可量化&#xff0c;是可确定的。 服务水平目标(SLO) SLO是目标&#x…

【树】哈夫曼树和哈夫曼编码

哈夫曼&#xff08;Huffman&#xff09;树&#xff0c;又称最优树&#xff0c;是一类带权路径长度最短的树。最优二叉树&#xff08;哈夫曼树&#xff09;路径&#xff1a;从树中一个结点到另一个结点之间的分支构成这两个结点之间的路。路径长度:路径上的分支数目&#xff1b;…

mysql分组排序取组内第一的数据行获取分组后,组内排名第一或最后的数据行。

前言&#xff1a; group by函数后取到的是分组中的第一条数据&#xff0c;但是我们有时候需要取出各分组的最新一条&#xff0c;该怎么实现呢&#xff1f; 本文提供两种实现方式。 一、准备数据 DROP TABLE IF EXISTS tb_dept; CREATE TABLE tb_dept (id bigint(20) UNSIG…

chat聊天系统消息消费时遇到的问题及优化思路

前言 之前有段工作经历涉及到了chat相关&#xff0c;而消息的发送 -> 存储 -> 消费是由不同的团队负责的&#xff0c;因此消息如何再多个团队之间流通、以及通过什么介质传递都是需要考虑的问题。 之前我负责过一些消息消费的相关工作&#xff0c;消息发送团队将消息推…

【Linux】简介磁盘|inode|动静态库

目录一.简介磁盘1.磁盘的物理结构&#xff1a;2.磁盘存储方式&#xff1a;3.磁盘的逻辑抽象&#xff1a;二.inode&&文件系统1.inode文件属性&#xff08;inode&#xff09;内容&#xff08;data block&#xff09;为什么删除一个文件相比于写一个文件要快得多&#xff…

若依配置教程(二)集成积木报表JimuReport

积木报表配置官网 在搭建好若依环境成功运行以后&#xff0c;我们先在这个系统中加一个小功能&#xff1a;JimuReport积木报表&#xff0c;以下步骤&#xff0c;我们按照官网教程&#xff0c;详细配置一下&#xff1a; 1.在ruoyi-admin文件夹下的pom.xml加入jar包依赖&#x…

MLP多层感知机理解

目录 .1简介 .2例子 2.1模型 2.2 实例 2.2.1 问题描述 2.2.2 数学过程 .3 代码 3.1 问题描述 3.2 代码 references&#xff1a; .1简介 多层感知机是全连接的 可以把低维的向量映射到高维度 MLP整个模型就是这样子的&#xff0c;上面说的这个三层的MLP用公式总结起来…

C 语言零基础入门教程(二十)

C 预处理器 C 预处理器不是编译器的组成部分&#xff0c;但是它是编译过程中一个单独的步骤。简言之&#xff0c;C 预处理器只不过是一个文本替换工具而已&#xff0c;它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器&#xff08;C Preprocessor&#x…

练手好福利!20个Python实战项目含源代码【2023最新】

高效学习源代码的步骤&#xff1a;1.运行程序&#xff0c;观察表现2.运行源码&#xff0c;断点调试&#xff0c;从头跟一边源码的执行流程&#xff0c;注意函数堆栈3.画类图、流程图&#xff0c;先把遇到的重要类记录下来&#xff0c;表明各个类的关系4.记录问题&#xff0c;把…

Unity XR

一、几个Unity XR Interaction Toolkit学习地址 1.B站视频 https://www.bilibili.com/video/BV11q4y1b74z/?spm_id_from333.999.0.0&vd_source8125d294022d2e63a58dfd228a7fcf63 https://www.bilibili.com/video/BV13b4y177J4/?spm_id_from333.999.0.0&vd_source8…

【对象的比较】java代码实现,详解对象的比较,Comparable接口和Comparator比较器

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#xff0c;&#x1f49e;&#x1f49e;&#x1f49e;今天的我们要学习的知识点是java对象的比较&#xff0c;不是大家现实生活中对象的比较&#xff0c;是java中new一个对象的那个对象&#xff0c;对象的比较到底是什么意思呢&…

24.网络编程(二)

目录 三.TCP通信 3.1 TCP协议特点 3.2 TCP协议通信场景 3.3 TCP通信模型演示 3.4 Socket 3.5 ServerSocket 3.6 注意事项 3.7 案例 3.7.1 TCP通信—单发单收 3.7.2 TCP通信—多发多收 3.7.3 TCP通信—同时接收多个客户端的消息。 3.7.4 TCP通信—使用线程池优化&am…

工业相机和镜头

工业相机和镜头镜头型号数据电源接口定焦镜头的调焦景深景深大小光圈相机、镜头选取参考镜头型号、数据电源接口、定焦镜头的调焦、景深、景深大小、光圈、相机、镜头选取 镜头型号 C&#xff0c;CS系列&#xff1a;相机镜头的C、CS接口非常相似&#xff0c;它们的接口直径、螺…

检索业务:基本数据渲染和排错

采用标签显示商品的数据 <div class"rig_tab"><div th:each"product:${result.getProducts()}"><div class"ico"><i class"iconfont icon-weiguanzhu"></i><a href"/static/search/#">…

5、数据的重构

目录 一、为什么进行数据重构 二、如何进行数据重构 一、为什么进行数据重构 进行数据分析时&#xff0c;有可能会发现数据的结构并不适合直接进行数据分析操作&#xff0c;如下面数据&#xff0c;但通过复制-粘贴-转置等方法操作又太繁琐&#xff0c;数据量小还行&#xff…