async/await简单介绍

在处理比较耗时的操作(如图片处理、数据压缩、http请求等)传统的异步方法是直接使用Thread或者Task进行操作,在复杂的应用编写中可能会出现回调的问题,因此C#目前主要推荐使用async/await来进行异步操作。也就是async/await主要用来异步回调问题, 而真正的异步操作还是用Task。

返回值

通常返回 Task 或 Task<TResult>。 在异步方法中,await 运算符应用于通过调用另一个异步方法返回的任务

如果方法包含指定 TResult 类型操作数的 return 语句,将 Task<TResult> 指定为返回类型,如果方法不含任何 return 语句或包含不返回操作数的 return 语句,将 Task 用作返回类型

异步方法也可以具有 void 返回类型。但是不推荐使用,因为无法等待具有 void 返回类型的异步方法,并且无效返回方法的调用方捕获不到异步方法引发的任何异常,而且也违背了我们使用他的初衷--解决异步回调问题

还需要注意:异步方法既不能声明任何 in、ref 或 out 参数,也不能具有引用返回值,但它可以调用具有此类参数的方法

实例

假如现在要做饭,需要做米饭2秒,做汤2秒;同步的方法就是先做米饭等待2秒,然后做汤等待2秒;异步的方法,米饭和汤同时做,一共花2秒

代码如下

class Program
{
private static Stopwatch stopwatch = new Stopwatch();
static void Main(string[] args)
{
stopwatch.Start();
DoCook();
Console.ReadLine();
}
/// <summary>
/// Cooking
/// </summary>
/// <returns></returns>
static async Task DoCook()
{
Console.WriteLine("Cook Start: " + stopwatch.ElapsedMilliseconds.ToString());
//case 1 异步Cooking
var rice = DoRice();
var soup = DoSoup();
await rice;
await soup;
Console.WriteLine($"Cook End: {stopwatch.ElapsedMilliseconds.ToString() } - {(rice.Result + soup.Result).ToString()}");
} /// <summary>
/// 做米饭,可以独立做
/// </summary>
/// <returns></returns>
static async Task<int> DoRice()
{
Console.WriteLine("DoRice Start: " + stopwatch.ElapsedMilliseconds.ToString());
var rice = ;
await Task.Run(() =>
{
Thread.Sleep();
rice = ;
});
Console.WriteLine("DoRice End: " + stopwatch.ElapsedMilliseconds.ToString());
return rice;
} /// <summary>
/// 做汤,可以独立做
/// </summary>
/// <returns></returns>
static async Task<int> DoSoup()
{
Console.WriteLine("DoSoup Start: " + stopwatch.ElapsedMilliseconds.ToString());
var soup = ;
await Task.Run(() =>
{
Thread.Sleep();
soup = ;
});
Console.WriteLine("DoSoup End: " + stopwatch.ElapsedMilliseconds.ToString());
return soup;
}
}

返回的结果

现在你女朋友很作,她非得吃蛋炒饭,蛋炒饭必须先做饭,假如做汤2秒,做蛋炒饭2秒钟,但是必须等米饭先做好

定义一个做蛋炒饭的方法

 /// <summary>
/// 做蛋炒饭,需要先做米饭
/// </summary>
/// <returns></returns>
static async Task<int> DoEggRice()
{
Console.WriteLine("DoEggRice Start: " + stopwatch.ElapsedMilliseconds.ToString());
var rice = await DoRice();
var eggRice = ;
await Task.Run(() =>
{
Thread.Sleep();
eggRice = rice + ;
});
Console.WriteLine("DoEggRice End: " + stopwatch.ElapsedMilliseconds.ToString());
return eggRice;
}

然后开始

        /// <summary>
/// Cooking
/// </summary>
/// <returns></returns>
static async Task DoCook()
{
Console.WriteLine("Cook Start: " + stopwatch.ElapsedMilliseconds.ToString());//case 3 异步EggCooking
var eggRice = DoEggRice();
var soup = DoSoup();
await eggRice;
await soup;
Console.WriteLine($"Cook End: {stopwatch.ElapsedMilliseconds.ToString() } - {(eggRice.Result + soup.Result).ToString()}");
}

结果如下

注意:

  1. 若某个函数F的函数体中需要使用await关键字的函数必须以async标记,进一步导致需要使用await调用F的那个函数也必须以async标记的情况
  2. await 表达式:用于异步方法内部,指出需要异步执行的任务。一个异步方法可以包含多个 await 表达式(不存在 await 表达式的话 IDE 会发出警告)
  3. var rice = DoRice();  rice.Result 属性为阻止属性。 如果你在其任务完成之前尝试访问它,当前处于活动状态的线程将被阻止,直到任务完成且值为可用。 在大多数情况下,应通过使用 await 访问此值,而不是直接访问属性
  4. 同步方法调用异步方法拿返回值会造成死锁.

引用问答

异步一定能提高效率吗?
不一定。异步本质上还是多线程,只是简化多线程的实现方式。至于使用多线程编程时能否提高程序执行效率,取决于 CPU 核心数,计算任务的复杂度以及该项任务本身是否适合被切分为并行计算模块。过于频繁地将不适合并行计算的任务拆分成异步编程中去,反而会导致密集计算性能的下降,因为此时线程池会疲于应对大量的线程调度操作。
有 async 一定要有 await 吗?

不一定。在标记为 async 的方法中,不必须出现 await 关键字,只是若没有 await 关键字,这个方法不是真正意义上的异步方法,它会与普通方法一样是同步执行的。编译器不会报错,但会给出提示。

相反,若要使用 await 关键字,则必须在方法签名中包含 async 关键字。否则 await 将被当做标识符,而不能被当做一个关键字来处理。也就是说,当一个方法的签名中不包含 async 关键字时,你甚至可以在方法体中把 await 作为变量名。但这种操作是极其不推荐的,很容易造成误导。

异步方法的名称一定要以「Async」为结尾吗?

不一定。这只是习惯问题,就跟微软推荐所有的自定义特性后面都以「Attributes」为结尾一样,这不是必须的,只是如果大家都这样做了,理解起来更加方便一些。具体情况取决于不同场合下的规范要求。

使用 Task 并且 Run 了之后就实现异步了吗?

不是,这只是进行了一次多线程操作,后面的语句还是同步执行的。直到遇见 await 关键字,随着控制权的返回,才真正能实现异步。

异步是线程安全的吗?

理论上是的,这也是为什么异步编程模型能够极大地简化传统多线程操作所带来的各种问题的一大原因。尽管 await 所指的对象运行在其他线程上,但其后的语句还是会在原始线程上被执行。更深层次地说,后续的语句实际上是使用 Task 的 ContinueWith 方法来实现的。所以我们大可以放心的在异步方法中修改诸如 UI 元素等由主线程管理的资源。

但是,异步编程模型只是简化了这个过程,而不能替代我们解决具体的数据同步问题。如果在 await 之后有对其他共享资源的访问,而在 await 获取执行结果之前,这些资源已经被其他线程修改,那么 await 后续语句执行时所面对的数据内容将是不可预测的。

异步一定是返回控制权与等待结果同时进行的吗?

第一时间返回控制权是一定的,而等待与否要看任务执行的状态。当程序遇到 await 关键字时,如果 Task 所指代的对象以极快的速度完成,那么异步方法内部就会以同步执行的方式继续向后执行 await 语句后面的操作,不会产生等待。只有当 Task 没有执行完毕时,才会进行等待

参考文献

https://blog.csdn.net/qc530167365/article/details/83108848

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/

https://www.jianshu.com/p/1136e79d96e6

https://www.jianshu.com/p/8ea7ed4a2493

C# .net async await 学习的更多相关文章

  1. C# async await 学习笔记2

    C# async await 学习笔记1(http://www.cnblogs.com/siso/p/3691059.html) 提到了ThreadId是一样的,突然想到在WinForm中,非UI线程 ...

  2. Async/Await 学习与示例

    参考:Async/await学习 es 7 提供了对 promise 对象的更好的操作,省去了很多丧心病狂的链式异步请求,promise 是回调地狱的福音,而 Async/Await 则是 promi ...

  3. ES8之async/await学习随笔

    详细学习参考文档: 阮一峰老师的博客,覆盖知识点ES6/7/8/9,本篇学习笔记对阮老师的关于async/await文档中的知识点进行分点总结 在ES8中加入async/await新特性后,很明显带来 ...

  4. async/await学习笔记

    async/await 的目的是简化使用 promises 的写法.     让我们来看看下面的例子: // 一个标准的 JavaScript 函数 function getNumber1() { r ...

  5. C# async await 学习笔记1

    由于我的开发工具为vs.net 2010(.net 4.0),需先做以下两步才能进行: 1.下载并安装Async CTP (http://www.microsoft.com/en-us/downloa ...

  6. C# async await 死锁问题总结

    可能发生死锁的程序类型 1.WPF/WinForm程序 2.asp.net (不包括asp.net mvc)程序 死锁的产生原理 对异步方法返回的Task调用Wait()或访问Result属性时,可能 ...

  7. .NET异步操作学习之一:Async/Await中异常的处理

    以前的异常处理,习惯了过程式的把出现的异常全部捕捉一遍,然后再进行处理.Async/Await关键字出来之后的确简化了异步编程,但也带来了一些问题.接下来自己将对这对关键字进行学习.然后把研究结果放在 ...

  8. JavaScript基础——深入学习async/await

    本文由云+社区发表 本篇文章,小编将和大家一起学习异步编程的未来--async/await,它会打破你对上篇文章Promise的认知,竟然异步代码还能这么写! 但是别太得意,你需要深入理解Promis ...

  9. Koa2学习(二)async/await

    Koa2学习(二)async/await koa2中用到了大量的async/await语法,要学习koa2框架,首先要好好理解async/await语法. async/await顾名思义是一个异步等待 ...

随机推荐

  1. PD ZD

    PD TO ZD simple one without . SORT FIELDS,,PD,A,,,PD,A) OUTREC FIELDS,,PD,ZD,LENGTH, ,,PD,ZD,LENGTH, ...

  2. lib 和 dll 的区别、生成以及使用详解 ~~包含示例代码~~(转)

    原文章地址:https://www.cnblogs.com/TenosDoIt/p/3203137.html#c 首先介绍一下静态库(静态链接库).动态库(动态链接库)的概念,首先两者都是代码共享的方 ...

  3. 项目到上传Gitee

    1.码云上创建一个项目 testgit (名字随你) 2.本地创建一个文件夹D:/testgit,然后使用git bash 3.cd 到本地文件夹中D:/testgit, 4.使用 git init ...

  4. 基本算数定理快速求约数个数——lightoj1208

    #include<bits/stdc++.h> using namespace std; #define ll long long #define maxn 1000005 bool vi ...

  5. kafka拦截器原理|案例实操

    拦截器原理 Producer拦截器(interceptor)是在Kafka 0.10版本被引入的,主要用于实现clients端的定制化控制逻辑. 对于producer而言,interceptor使得用 ...

  6. NX二次开发-读取图纸表格注释与部件属性关联的名字

    NX11+VS2013 #include <uf.h> #include <uf_ui.h> #include <uf_tabnot.h> #include < ...

  7. 如何理解Vue的render函数

    第一个参数(必须) - {String | Object | Function} <!DOCTYPE html> <html lang="en"> < ...

  8. Unity shader之金属质感衣服

    一套QQ飞车的衣服,模仿其效果写的shader,效果如下: 部分shader如下: Shader "qq/Cloth" { Properties { _MainTex (" ...

  9. 次梯度(Subgradient)

    参考链接:https://closure11.com/subgradient/

  10. 【转载】C# 开源库大全非常好

    原文地址:http://m.blog.csdn.net/woddle/article/details/37311877 C#开源大全 商业协作和项目管理平台-TeamLab 网络视频会议软件-VMuk ...