前言

扯淡了 17 篇,这篇终于开始学习 async 和 await 了,有了前面的基础,来理解 async 和 await 就容易理解多了。

这一篇一定要按照每一个示例,去写代码、执行、输出结果,自己尝试分析思路。

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 ,在官方文档中的说明,就这么多。

从以往知识推导

这里,你会跟笔者从以往文章中学习到的知识,去推导,去理解 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

TaskTask<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);
}

至此,理解为什么使用了 asyncawait,执行时还是同步了吧?

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。

{{uploading-image-192132.png(uploading...)}}

为什么出现一层层的 await

这是相对于提供服务者来说。因为我要提供接口给你使用,因此底层出现 async、await 后,我会继续保留方法是异步的(async),然后继续封装,这样就有多层的调用结构,例如上一小节的图。

但是如果来到了调用者这里,就不应该还是使用 async 、await 去编写方法,而是应该按照实际情况同步或异步。

通过本篇文章,理解 async 和 await 了吧?

C# 多线程(18):一篇文章就理解async和await的更多相关文章

  1. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  2. 第十五节:深入理解async和await的作用及各种适用场景和用法

    一. 同步VS异步 1.   同步 VS 异步 VS 多线程 同步方法:调用时需要等待返回结果,才可以继续往下执行业务 异步方法:调用时无须等待返回结果,可以继续往下执行业务 开启新线程:在主线程之外 ...

  3. 理解async和await

    async 是“异步”的简写,而 await 可以认为是 async wait 的简写. 所以应该很好理解 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执 ...

  4. 【Python】关于Python多线程的一篇文章转载

    猪哥推荐的学习网址 http://www.jb51.net/article/110164.htm yeayee ------>更多技巧------>更多源码------>http:/ ...

  5. 一篇文章彻底理解Redis持久化:RDB和AOF

    为什么需要持久化? Redis对数据的操作都是基于内存的,当遇到了进程退出.服务器宕机等意外情况,如果没有持久化机制,那么Redis中的数据将会丢失无法恢复.有了持久化机制,Redis在下次重启时可以 ...

  6. 深入理解async和await的作用及各种适用场景和用法

    https://www.cnblogs.com/yaopengfei/archive/2018/07/02/9249390.html https://www.cnblogs.com/xianyudot ...

  7. 那些年我们一起追逐的多线程(Thread、ThreadPool、委托异步调用、Task/TaskFactory、Parallerl、async和await)

    一. 背景 在刚接触开发的头几年里,说实话,根本不考虑多线程的这个问题,貌似那时候脑子里也有没有多线程的这个概念,所有的业务都是一个线程来处理,不考虑性能问题,当然也没有考虑多线程操作一条记录存在的并 ...

  8. async和await浅析

    要理解async和await的用法,首先要了解Task相关知识,这里不做说明,因为这不是本文的重点. 如果你已经对Task很了解,那么如何使用async和await,在此主要总结了以下三点: 只有在a ...

  9. async和await用法

    原文:async和await用法 要理解async和await的用法,首先要了解Task相关知识,这里不做说明,因为这不是本文的重点. 如果你已经对Task很了解,那么如何使用async和await, ...

随机推荐

  1. 8.1 api概述及使用

    api文件:JDK_API_1.6.CHM 1:打开帮助文档2:点击显示,找到索引,看到输入框3:你要学习什么内容,你就在框框里面输入什么内容 举例:Random4:看包 java.lang包下的类在 ...

  2. Struts2-学习笔记系列(15)-ajax支持和JSON

    7.1stream类型的result 使用stream就无需jsp页面,直接在action想浏览者生成指定的响应 @Override public java.lang.String execute() ...

  3. MODIS系列之NDVI(MOD13Q1)四:MRT单次及批次处理数据

    前言: 本篇文章的出发点是因为之前接触过相关研究,困囧于该系列资料匮乏,想做一个系列.个人道行太浅,不足之处还请见谅.愿与诸君共勉. 数据准备: MODIS数据产品MOD13Q1—以2010年河南省3 ...

  4. Java成长第三集--基础重点详细说明

    接上篇文章,继续阐述相关的重点基础知识,话不多说! 一.Java中equals()和“==”区别 1.对于8种基础数据类型,使用“=="比较值是否相等: 2.对于复合数据类型(类),使用eq ...

  5. Volatile不保证原子性(二)

    Volatile不保证原子性 前言 通过前面对JMM的介绍,我们知道,各个线程对主内存中共享变量的操作都是各个线程各自拷贝到自己的工作内存进行操作后在写回到主内存中的. 这就可能存在一个线程AAA修改 ...

  6. Nikto使用方法

    Introduction Nikto是一款开源的(GPL)网站服务器扫描器,使用Perl基于LibWhisker开发.它可以对网站服务器进行全面的多种扫描,包括6400多个潜在危险的文件/CGI,检查 ...

  7. 第八节:time和random模块

    定义: 模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块使用. 重点: 1.模块的名字不要和自带的模块名字相同,不然会优先调用自己的那个模块,因为查找模块的时候是按照sys.pat ...

  8. Threejs从入门到入门

    前言threejs官网:https://threejs.org/ github各个版本:https://github.com/mrdoob/three.js/tags 版本更迭很快,我用的时候还是r9 ...

  9. Python 小技之实现的鲜花盛宴,你准备好了吗?

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:派森酱 PS:如有需要Python学习资料的小伙伴可以加点击下方链接 ...

  10. CSS属性中的display属性浅谈;

    首先我们要知道什么是块级元素和行内元素有什么区别: 承接上篇文章:(浅谈HTML和body标签) 块级元素:浏览器解析为独占一行的元素(例如:div.table.ul等.),浏览器会在该元素的前后显示 ...