一篇文章弄懂C#中的async和await
作者:痴者工良 发布时间:2021-08-15 16:41:14
目录
前言
async
await
从以往知识推导
创建异步任务
创建异步任务并返回Task
异步改同步
说说 await Task
说说 async Task<TResult>
同步异步?
Task封装异步任务
关于跳到 await 变异步
为什么出现一层层的 await
总结
前言
本文介绍async/Task。在学习该知识点过程中,一定要按照每一个示例,去写代码、执行、输出结果,自己尝试分析思路。
async
微软文档:使用 async 修饰符可将方法、lambda 表达式或匿名方法指定为异步。
使用 async 修饰的方法,称为异步方法。
例如:
为了命名规范,使用 async 修饰的方法,需要在方法名称后面加上 Async 。
public async Task<int> TestAsync()
{
// . . . .
}
Lambda :
static void Main()
{
Thread thread = new Thread(async () =>
{
await Task.Delay(0);
});
}
public static async Task<int> TestAsync() => 666;
await
微软文档:await 运算符暂停对其所属的 async 方法的求值,直到其操作数表示的异步操作完成。
异步操作完成后,await 运算符将返回操作的结果(如果有)。
好的,到此为止,async 和 await ,在官方文档中的说明,就这么多。
从以往知识推导
这里我们不参考文档和书籍的资料,不要看文档和书籍中的示例,我们要一步步来从任务(Task)中的同步异步开始,慢慢摸索。去分析 async 和 await 两个关键字给我们的异步编程带来了什么样的便利。
创建异步任务
场景:周六日放假了,可以打王者(一种游戏),但是昨天的衣服还没有洗;于是用洗衣机洗衣服,清洗期间,开一局王者(一种游戏)。
我们可以编写一个方法如下:
static void Main()
{
Console.WriteLine("准备洗衣服");
// 创建一个洗衣服的任务
Task<int> task = new Task<int>(() =>
{
// 模拟洗衣服的时间
int time = new Random().Next(2, 6);
Thread.Sleep(TimeSpan.FromSeconds(time));
return time;
});
Console.WriteLine("开始洗衣服");
// 让洗衣机洗衣服
task.Start();
Console.WriteLine("我去打王者,让洗衣机洗衣服");
// 打王者
Thread.Sleep(TimeSpan.FromSeconds(4));
Console.WriteLine("打完王者了,衣服洗完了嘛?");
Console.WriteLine(task.IsCompleted);
if (task.IsCompleted)
Console.WriteLine("洗衣服花的时间:" + task.Result);
else
{
Console.WriteLine("在等洗衣机洗完衣服");
task.Wait();
Console.WriteLine("洗衣服花的时间:" + task.Result);
}
Console.WriteLine("洗完了,捞出衣服,晒衣服,继续打王者去");
}
创建异步任务并返回Task
上面的示例,虽然说,异步完成了一个任务,但是这样,将代码都放到 Main ,可读性十分差,还要其它什么规范之类的,不允许我们写这样的垃圾代码。于是我们将洗衣服这个任务,封装到一个方法中,然后返回 Task 即可。
在 Program 类中,加入如下一个方法,这个方法用于执行异步任务,并且返回 Task 对象。
/// <summary>
/// 执行一个任务
/// </summary>
/// <returns></returns>
public static Task<int> TestAsync()
{
Task<int> task = new Task<int>(() =>
{
// 模拟洗衣服的时间
int time = new Random().Next(2, 6);
Thread.Sleep(TimeSpan.FromSeconds(time));
return time;
});
task.Start();
return task;
}
Main 方法中,改成
static void Main()
{
Console.WriteLine("准备洗衣服");
// 创建一个洗衣服的任务
Task<int> task = TestAsync();
... ...
但是,两者差别还是不大。
异步改同步
我们创建了异步方法,去执行一个洗衣服的任务;当打完游戏后,需要检查任务是否完成,然后才能进行下一步操作,这时候就出现了 同步。为了保持同步和获得执行结果,我们使用了 .Wait() 、.Result 。
这里我们尝试将上面的操作转为同步,并且获得执行结果。
class Program
{
static void Main()
{
int time = Test();
// ... ...
}
/// <summary>
/// 执行一个任务
/// </summary>
/// <returns></returns>
public static int Test()
{
Task<int> task = new Task<int>(() =>
{
// 模拟洗衣服的时间
int time = new Random().Next(2, 6);
Thread.Sleep(TimeSpan.FromSeconds(time));
return time;
});
task.Start();
return task.Result;
}
}
说说 await Task
Task 和 Task<TResult> ,前者是一个没有返回结果的任务,后者是有返回结果的任务。前面的文章中已经使用过大量的示例,这里我们使用 await ,去完成一些完全相同的功能。
Task:
public static void T1()
{
Task task = new Task(() => { });
task.Wait();
}
public static async void T2()
{
Task task = new Task(() => { });
await task;
}
说明,await 可以让程序等待任务完成。
Task<TResult>:
public void T3()
{
// 获取 Task 任务对象,后面的逻辑过程可以弄成异步
Task<int> task = TestAsync();
// 任务是异步在执行,我不理会他
// 这里可以处理其它事情,处理完毕后,再获取执行结果
// 这就是异步
Console.WriteLine(task.Result);
}
public async void T4()
{
// 使用 await 关键字,代表等待执行完成,同步
int time = await TestAsync();
Console.WriteLine(time);
}
说明:await 可以让程序等待任务执行完成,并且获得执行结果。
看到没有。。。await 关键字,作用是让你等,是同步的,压根不是直接让你的任务变成异步后台执行的。
那为啥提到 async 、await,都是说跟异步有关?不急,后面解释。
说说 async Task<TResult>
async Task<TResult> 修饰一个方法,那么这个方法要返回 await Task<TResult> 的结果。
两种同步方式示例对比:
public static int Test()
{
Task<int> task = new Task<int>(() =>
{
// 模拟洗衣服的时间
int time = new Random().Next(2, 6);
Thread.Sleep(TimeSpan.FromSeconds(time));
return time;
});
task.Start();
return task.Result;
}
public static async Task<int> TestAsync()
{
Task<int> task = new Task<int>(() =>
{
// 模拟洗衣服的时间
int time = new Random().Next(2, 6);
Thread.Sleep(TimeSpan.FromSeconds(time));
return time;
});
task.Start();
int time = await task;
return time;
}
同步异步?
问:async 和 await 不是跟异步方法有关嘛,为啥前面的示例使用了 await ,全部变成同步了?
问:使用 async 和 await 的方法,执行过程到底是同步还是异步?
答:同步异步都行,要同步还是异步,全掌握在你的手上。
你使用 await 去调用一个异步方法,其执行过程就是同步。
你获取异步方法返回的 Task,就是异步。
最近笔者收到一些提问,有些读者,使用 async 和 await 去编写业务,想着是异步,可以提升性能,实际结果还是同步,性能一点没有提升。通过下面的示例,你会马上理解应该怎么用。
首先,在不使用 async 和 await 关键字的情况下,我们来编写两个方法,分别实现同步和异步的功能,两个方法执行的结果是一致的。
/// <summary>
/// 同步
/// </summary>
/// <returns></returns>
public static int Test()
{
Task<int> task = new Task<int>(() =>
{
return 666;
});
task.Start();
return task.Result;
}
/// <summary>
/// 异步
/// </summary>
/// <returns></returns>
public static Task<int> TestAsync()
{
Task<int> task = new Task<int>(() =>
{
return 666;
});
task.Start();
return task;
}
能不能将两个方法合并在一起呢?想同步就同步,想异步就异步,这样就不需要写两个方法了!
是可以的!通过 async 和 await 关键字,可以轻松实现!
合并后,代码如下:
/// <summary>
/// 可异步可同步
/// </summary>
/// <returns></returns>
public static async Task<int> TestAsync()
{
Task<int> task = new Task<int>(() =>
{
return 666;
});
task.Start();
return await task;
}
合并后,我们又应该怎么在调用的时候,实现同步和异步呢?
笔者这里给出两个示例:
// await 使得任务同步
public async void T1()
{
// 使用 await 关键字,代表等待执行完成,同步
int time = await TestAsync();
Console.WriteLine(time);
}
// 直接获得返回的 Task,实现异步
public void T2()
{
// 获取 Task 任务对象,后面的逻辑过程可以弄成异步
Task<int> task = TestAsync();
// 任务是异步在执行,我不理会他
// 这里可以处理其它事情,处理完毕后,再获取执行结果
// 这就是异步
Console.WriteLine(task.Result);
}
至此,理解为什么使用了 async 和 await,执行时还是同步了吧?
Task封装异步任务
前面,我们都是使用了 new Task() 来创建任务,而且微软官网大多使用 Task.Run() 来编写 async 和 await 的示例。
因此,我们可以修改前面的异步任务,改成:
/// <summary>
/// 可异步可同步
/// </summary>
/// <returns></returns>
public static async Task<int> TestAsync()
{
return await Task.Run<int>(() =>
{
return 666;
});
}
关于跳到 await 变异步
在百度学习异步的时候,往往会有作者说,进入异步方法后,同步执行代码,碰到 await 后就是异步执行。
当然还有多种说法。
我们已经学习了这么多的任务(Task)知识,这一点十分容易解释。
因为使用了 async 和 await 关键字,代码最深处,必定会出现 Task 这个东西,Task 这个东西本来就是异步。碰到 await 出现异步,不是因为 await 的作用,而是因为最底层有个 Task。
为什么出现一层层的 await
这是相对于提供服务者来说。因为我要提供接口给你使用,因此底层出现 async、await 后,我会继续保留方法是异步的(async),然后继续封装,这样就有多层的调用结构,例如上一小节的图。
但是如果来到了调用者这里,就不应该还是使用 async 、await 去编写方法,而是应该按照实际情况同步或异步。
来源:https://mp.weixin.qq.com/s/53EgxZHzEZJvbRmhaXA8eg


猜你喜欢
- 一、简介地图控件自v2.3.5版本起,支持多实例,即开发者可以在一个页面中建立多个地图对象,并且针对这些对象分别操作且不会产生相互干扰。文件
- Threadlocal有什么用:简单的说就是,一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的(每个线程都只能看到自
- 先看效果图一、申请成为百度开发者,获得使用地图API接口的权限,获取(AK)码。1.打开百度地图开放平台打开网址:http://lbsyun
- 使用类的全权名: System.Text.StringBuilder sb = new System.Text.StringBuilder(
- 使用开源框架是,可以直接复制源代码到自己的项目(本人在Android Studio中操作报R程序包不存在),也可以使用jar包,下面记录一下
- 何为高阶函数 大家可能对这个名词并不熟悉,但是这个名词所表达的事物却是我们经常使用到的。只要我们的函
- 前言底部Tab已经是一个应用的标配了,因为手机屏幕大小的限制,使得我们必须去最大化的利用可见的空间。当然底部Tab一般为3个左右,最多不会超
- 本文实例讲述了Java实现的傅里叶变化算法。分享给大家供大家参考,具体如下:用JAVA实现傅里叶变化 结果为复数形式 a+bi废话不多说,实
- 这个破碎动画,是一种类似小米系统删除应用时的 * 破碎效果的动画。效果图展示先来看下是怎样的动效,要是感觉不是理想的学习目标,就跳过,避免浪费
- 本文实例讲述了Java实现爬取百度图片的方法。分享给大家供大家参考,具体如下:在以往用java来处理解析HTML文档或者片段时,我们通常会采
- Java线程的生命周期的详解对于多线程编程而言,理解线程的生命周期非常重要,本文就针对这一点进行讲解。一、线程的状态线程的存在有几种不同的状
- 本文实例讲述了Java编程实现提取文章中关键字的方法。分享给大家供大家参考,具体如下:实现代码:/** * 相关的jar包 * lucene
- 本文实例为大家分享了Android App获取屏幕旋转角度的具体代码,供大家参考,具体内容如下一、获取屏幕旋转角度的方法是:int rota
- 在去年的时候,在各种渠道中略微的了解了SpringBoot,在开发web项目的时候是如何的方便、快捷。但是当时并没有认真的去学习下,毕竟感觉
- 1. 概述官方JavaDocsApi: javax.swing.JComboBoxJComboBox,下拉列表框。JComboBox以下列列
- Java读取properties文件中文乱码初用properties,读取java properties文件的时候如果value是中文,会出
- 由 CardLayout 类实现的布局管理器称为卡片布
- 在平时做系统项目时,经常会需要做导出功能,不论是导出excel,还是导出cvs文件。我下面的demo是在springmvc的框架下实现的。1
- 一、引言Good Good Study,Day Day Up,童鞋点个关注,不迷路,么么哒~~~MP自带的条件构造器虽然很强大,有时候也避免
- java 遍历listpackage com.tiandy.core.rest;import java.util.ArrayList;imp