一.  FrameWork 4.0之前的线程世界    

    在.NET FrameWork 4.0之前,如果我们使用线程。一般有以下几种方式:

  • 使用System.Threading.Thread 类,调用实例方法Start()开启一个新线程,调用Abort()方法来提前终止线程。
  • 使用System.Threading.ThreadPool类,调用静态方法QueueUserWorkItem(),将方法放入线程池队列,线程池来控制调用。
  • 使用BeginInvoke,EndInvoke,BeginRead,EnRead,BeginWrite,EndWrite等一系列的异步方法。
  • 使用System.ComponentModel.BackgroundWorker控件,调用实例方法RunWorkerAsync(),开启一个新线程。 

二.  .Net 传统异步编程概述 

  • 异步编程模型 (APM),在该模型中异步操作由一对 Begin/End 方法(如 FileStream.BeginRead 和 Stream.EndRead)表示。
  • 基于事件的异步模式 (EAP),在该模式中异步操作由名为“操作名称Async”和“操作名称Completed”的方法/事件对(例如 WebClient.DownloadStringAsync 和 WebClient.DownloadStringCompleted)表示。 (EAP 是在 .NET Framework 2.0 版中引入的,在silverlight或者wpf变成中经常用到)。

三.  Task 的优点以及功能   

  • 在任务启动后,可以随时以任务延续的形式注册回调。
  • 通过使用 ContinueWhenAll 和 ContinueWhenAny 方法或者 WaitAll 方法或 WaitAny 方法,协调多个为了响应 Begin_ 方法而执行的操作。
  • 在同一 Task 对象中封装异步 I/O 绑定和计算绑定操作。
  • 监视 Task 对象的状态。
  • 使用 TaskCompletionSource 将操作的状态封送到 Task 对象。

众所周知,async方法只可以返回void,Task和Task<T>。

对于返回void的async方法,它并不是awaitable,所以其他方法不能用await方法来调用它,而返回Task的async方法则可以。

那么当async方法返回Task后,接着await,那被await的Task是一个什么概念?是async方法中第一个被await的Task?不,它代表目标async方法的全部执行,其中包括被await分割的连接Task,但是不包括非await造成的多线程执行。

如下代码,在doo是一个返回Task的async方法,然后在另一个方法test中await调用doo,然后在Main方法中调用test(由于Main方法不允许加async,所以需要另外加一个async方法来使用await)

static void Main(string[] args)

{

test();

log("Main:调用test后");

Thread.Sleep(Timeout.Infinite);

}

//Main方法不允许加async,所以我们用这个方法使用await

static async void test()

{

log("test: await之前");

await doo();

log("test: await之后");

}

//返回Task的async方法

static async Task doo()

{

log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 1; }));

log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 2; }));

log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 3; }));

Thread.Sleep(1000);

Console.WriteLine("doo中在Task外的Thread.Sleep执行完毕");

}

//输出方法:显示当前线程的ManagedThreadId

static void log(string msg)

{

Console.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, msg);

}

上面代码会输出:

1: test: await之前

1: Main:调用test后

3: Task

3: doo: Task结果:1

4: Task

4: doo: Task结果:2

3: Task

3: doo: Task结果:3

doo中在Task外的Thread.Sleep执行完毕

3: test: await之后

前两句简单,调用test方法,await后的内容会被加在目标Task的后面,然后test马上返回,于是输出“Main:调用test后”,同时他们都是在主线程中执行的,所以ManagedThreadId都是1。

接着后面就是另一个Task的执行(当然在另一个线程,也是test方法中await的目标Task)。这个所谓的Task就是doo方法的全部执行。所以doo中三个顺序执行的Task(通过await一个一个连接)依次执行,所以Task输出结果1,2,3。第一个Task的ManagedThreadId是3,第二个是4,第三个又是3,原因是Task的内部执行使用了CLR的线程池,所以线程得到了重复利用。

接着doo方法还没有完,最后一个await造成doo方法后面的代码在这个await针对的Task执行后继续执行,于是输出:doo中Task外的Thread.Sleep执行完毕。

最后当doo彻底执行完test的await才结束,所以最后一行输出:test:await之后。

上面我说过:被await的async方法返回的Task代表“目标async方法的全部执行,其中包括被await分割的连接Task,但是不包括非await造成的多线程执行”。

所以如果把返回Task的async方法(也就是上例中的doo方法)改成这样:

//返回Task的async方法

static async Task doo()

{

log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 1; }));

log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 2; }));

log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 3; }));

//不使用await:线程池多线程

ThreadPool.QueueUserWorkItem(_ =>

{

Thread.Sleep(1000);

Console.WriteLine("ThreadPool.QueueUserWorkItem");

});

//不使用await:Task多线程

Task.Run(() =>

{

Thread.Sleep(1000);

Console.WriteLine("Task.Run");

});

}

我们加入了不用await的多线程执行,分别使用ThreadPool和Task,整个程序会输出这样的结果:

1: test: await之前

1: Main:调用test后

3: Task

3: doo: Task结果:1

4: Task

4: doo: Task结果:2

3: Task

3: doo: Task结果:3

3: test: await之后

Task.Run

ThreadPool.QueueUserWorkItem

不使用await的多线程完全脱离了test方法中await的Task,是运行在test的await之后的。

另外Visual Studio会对Task.Run代码做如下警告:

提示:Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the ‘await’ operator to the result of the call.

就是说,如果不加await,当前方法会继续执行直到结束,不用管他,因为我们现在就是在做在async方法中不用await的测试,呵呵。

或许你会问,为什么要用这样的方式去await另一个async方法返回的Task呢?我们一直在讨论返回Task的async方法,我认为看一个返回Task<T>的async方法可以更好地解释这个问题。

下面我们把上面的代码改成相似的返回Task<int>的async方法执行,那么doo方法返回Task<T>,他把自己方法内3个awaited Task的结果统一相加,最后返回结果并作为自己返回的Task的结果。然后在test方法中输出doo返回的结果。

完整代码:

static void Main(string[] args)

{

test();

log("Main:调用test后");

Thread.Sleep(Timeout.Infinite);

}

//Main方法不允许加async,所以我们用这个方法使用await

static async void test()

{

log("test: await之前");

Console.WriteLine("doo结果:{0}", await doo());

log("test: await之后");

}

//返回Task的async方法

static async Task<int> doo()

{

var res1 = await Task.Run(() => { Thread.Sleep(1000); log("awaited Task1执行"); return

var res2 = await Task.Run(() => { Thread.Sleep(1000); log("awaited Task2执行"); return

var res3 = await Task.Run(() => { Thread.Sleep(1000); log("awaited Task3执行"); return

//不使用await:线程池多线程

ThreadPool.QueueUserWorkItem(_ =>

{

Thread.Sleep(1000);

Console.WriteLine("ThreadPool.QueueUserWorkItem");

});

//不使用await:Task多线程

Task.Run(() =>

{

Thread.Sleep(1000);

Console.WriteLine("Task.Run");

});

return res1 + res2 + res3;

}

//输出方法:显示当前线程的ManagedThreadId

static void log(string msg)

{

Console.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, msg);

}

先看结果:

1: test: await之前

1: Main:调用test后

3: awaited Task1执行

4: awaited Task2执行

4: awaited Task3执行

doo结果:6

4: test: await之后

ThreadPool.QueueUserWorkItem

Task.Run

和上一个返回Task的例子一样,当在test方法中await doo方法返回的Task,doo内awaited Task都被先等了,而没有awaited的线程都并没有被等,这是为什么呢(也就是上面留下的那个问题)?下面用这个返回Task<int>的例子解释一下:

在test中await doo返回的Task,那么此时我们需要他的结果,而他的结果是需要自己方法内所包含的其他awaited结果,可以理解成被等的子结果。所以自己的结果需要其他的结果,那么等这个结果必须需要等那些被依赖的结果也出来。所以test方法await doo方法的结果会同样等待所有doo内的await,不会管其他doo内非await的多线程执行(当然从技术角度讲,也是不可能的,因为async/await可以这样全靠的是编译器)。

摘自 Mgen

转载于:https://www.cnblogs.com/xizz/p/5009413.html

.NET(C#):await返回Task的async方法相关推荐

  1. NET(C#):await返回Task的async方法

    一.  FrameWork 4.0之前的线程世界    在.NET FrameWork 4.0之前,如果我们使用线程.一般有以下几种方式: 使用System.Threading.Thread 类,调用 ...

  2. c# task添加顺序_关于c#:Task和async等待所需的指导

    本问题已经有最佳答案,请猛点这里访问. 在浏览了许多文章和视频后,我仍然对异步编程有疑问.我正在一个项目中,在服务层中,我已将所有方法创建为异步方法.所有返回Task < T >或Task ...

  3. Task和async/await详解

    一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...

  4. C#多线程和异步(二)——Task和async/await详解

    一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...

  5. 在.NET中执行Async/Await的两种错误方法

    微信公众号:架构师高级俱乐部 关注可了解更多的编程,架构知识.问题或建议,请公众号留言; 如果你觉得此文对你有帮助,欢迎转发 在.NET中执行异步/等待的两种错误方法 在应用开发中,我们为了提高应用程 ...

  6. 【转】异步编程系列(Thread、Task、async/await、ajax等)

    序 经过一番努力,我写的异步编程系列也算有头有尾,当然不是说这个系列已经更新完毕,这个头尾只是表示新旧知识点都有简单涉及到,接下去我还会丰富这一系列并且有机会整个小应用(愿景是弄一个开源组件吧,结合s ...

  7. 【转】Task和async/await详解

    一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...

  8. C# Task和async/await详解

    C# Task和async/await详解 什么是异步 Task介绍 1 Task创建和运行 2 Task的阻塞方法(Wait/WaitAll/WaitAny) 1 Thread阻塞线程的方法 2 T ...

  9. C#多线程和异步(二)——Task和async/await详解(转载)

    一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...

最新文章

  1. LeetCode简单题之Excel 表中某个范围内的单元格
  2. CVPR 2021 | 基于语义聚合与自适应2D-1D配准的手部三维重建(快手)
  3. R语言基于Bagging算法(融合多个决策树)构建集成学习Bagging分类模型、并评估模型在测试集和训练集上的分类效果(accuray、F1、偏差Deviance):Bagging算法与随机森林对比
  4. springboot(二):web综合开发
  5. php通过浏览器下载json文件遇到的问题
  6. .NET core3.0 使用Jwt保护api
  7. deprecated conversion from string constant to ‘char*’
  8. FPGA SPI总线协议简介
  9. 一种去水印的营业执照识别方法
  10. 网吧服务器是起什么作用的,网吧服务器的用途是什么?
  11. Softlink Hardlink
  12. Xcode 建立 UIKit 项目(Hello World)
  13. 关于阻止迅雷上传,带慢计算机的工具
  14. 进程学习:进程间通信(传统通信方式)1.无名管道
  15. [Java开发]搭建人力资源管理系统——简历管理模块(附带下载链接)
  16. 你所浪费的今天,是昨天死去的人奢望的明天。你所厌恶的现在,是未来的你回不去的曾经
  17. Angular2+ 面试题集锦
  18. Ubuntu下解决Make的:cc1plus: warnings being treated as errors
  19. linux指纹登录实现原理,指纹识别技术原理与基于Linux系统的指纹识别门禁系统设计...
  20. 编译原理(十八)——运行时存储空间管理

热门文章

  1. Visual Studio 2019 发行说明
  2. 试算平衡表示例图_期末试算平衡表截图
  3. 20155325 Exp4 恶意代码分析
  4. LiveData 的基本使用
  5. 【论文阅读01】2021 兵棋推演的智能决策技术与挑战 尹奇跃
  6. excel如何用超链接直接打开文件
  7. 网页中漂浮的图片代码
  8. iphone各机型的分辨率及像素
  9. 小游戏如何带动直播平台发展?
  10. 在微信里接收文件后如何指着文件用咱编的APP打开?