前言

扯淡了 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. javascript入门 之 Ajax(一)

    1.在项目的根目录下创建data目录,data目录下创建info文件,编写info文件如下代码: <h1>all data<h2> <p>this is the d ...

  2. 第二章 IBM

    1.不要招惹IBM,这可是一家做过军火的公司,一家参与了曼哈顿计划的公司. 同时也是世界上最大的服务公司.第二大软件公司.第二大数据库公司.拥有工业界最大的实验室.第一专利申请大户.最大的开源linu ...

  3. 10.2 io流 之字节流和字符流

    FileWriter 用于写入字符流.要写入原始字节流,请考虑使用 FileOutputStream. io流相关文档: https://www.cnblogs.com/albertrui/p/836 ...

  4. python调用js

    安装 pip install PyExecJS 方法 eval() 输入参数:source(JS语句).cwd(路径) 返回值:result(语句执行结果) compile() 输入参数:source ...

  5. 运行jmeter.bat时 提示 not able to find java executable or version

    安装过好几次,这是第一次遇到运行jmeter.bat时 提示 not able to find java executable or version Please check your Java in ...

  6. 练习,自定义TextView(1.1)

    重新自定义TextView是非常有趣的事情,跟着Android4高级编程,通过自定义TextView,来敲一下代码: 这个是那么的简单,自定义TextView,新建CustomTextView继承Te ...

  7. 深度解密 Go 语言之 sync.Pool

    最近在工作中碰到了 GC 的问题:项目中大量重复地创建许多对象,造成 GC 的工作量巨大,CPU 频繁掉底.准备使用 sync.Pool 来缓存对象,减轻 GC 的消耗.为了用起来更顺畅,我特地研究了 ...

  8. stand up meeting 12/9/2015

    part 组员 今日工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云  --------------    --  -----------  -- PDF Reader 朱玉影 SDK终于差不 ...

  9. Dae-Da-Lus小组idea集锦

    Dae-Da-Lus小组成员经过认真的思考,每一位同学都提出了自己对于Team Project的想法,暂时Mark在这里,以备查阅~ 曹士杰: 作为一个计算机专业的学生,我想我们应该是幸运的.计算机科 ...

  10. 数据结构与算法--二分搜索(binary search)

    前言 之前面试准备秋招,重新翻起了<编程之美>.在第三章节看到了一道关于二分搜索的讨论,觉得有许多细节是自己之前也没怎么特别注意地方,比如二分搜索的初始条件,转化.终止条件之类的. 问题 ...