C#中的异步编程--探索await与async关键字的奥妙之处,原来理解和使用异步编程可以这么简单
前言
await与async是C#5.0推出的新语法,关于await与async有很多文章讲解。但看完后有没有这样一种感觉,感觉这东西像是不错,但好像就是看不太懂,也不清楚该怎么使用。虽然偶有接触,但是一直都没有真正搞明白。
我也是才刚刚摸索明白,把学习结果和大家探讨一下看掌握得对不对。个人的学习习惯就是,有复杂的东西可以简单说明白,就会分享出来~
(阅读本文需要具备多线程及任务编程的基础)
重点
在学习async/await最难的是什么呢?就是理解它的工作方式!
1.所有的async方法返回类型必然是Task或Task<T>,这是异步处理的基础!
2.在async方法中遇到await关键字后,当前线程立即返回(到调用方),继续之前的处理逻辑;await关键字之后的代码逻辑,将交由新的线程处理;当新的线程处理完成后,可以从新的线程返回处理结果到调用(处)线程当中,结束等待。
3.在一个async方法中,会根据await关键字进行分割,拆分到不同的线程处理同一个方法的不同部分!
4.把一个方法代码的不同部分拆分到多个线程处理,这是异步方法和同步方法的最大不同!
把上面几点搞明白了,其实异步编程也就大概清楚了吧。。
简单异步调用
class Program
{
static void Main(string[] args)
{
Console.WriteLine("{0}->Main.异步方法执行前", Thread.CurrentThread.ManagedThreadId.ToString());//输出异步处理之前的线程ID
DoAsync().Wait();//执行异步处理,并等待该异步方法执行完成后才继续
Console.WriteLine("{0}->Main.异步方法执行后", Thread.CurrentThread.ManagedThreadId.ToString());//输出异步处理之后的线程ID Console.Read();
} /// <summary>
/// 执行异步处理
/// </summary>
/// <param name="times">模拟处理时长</param>
/// <returns></returns>
public static async Task DoAsync(int times)
{
Console.WriteLine("{0}->DoAsync.await之前", Thread.CurrentThread.ManagedThreadId.ToString());//输出调用线程ID
await Task.Run(() => Thread.Sleep(times));///执行一个异步任务,并等待返回结果才继续;需要注意的是,调用线程执行到这一行的时候其实就已经返回了
Console.WriteLine("{0}->DoAsync.await之后", Thread.CurrentThread.ManagedThreadId.ToString());//异步操作执行完了,但这里已经是新的线程了
}
}
->Main.异步方法执行前
->DoAsync.await之前
->DoAsync.await之后
->Main.异步方法执行后
请留意异步方法DoAsync的处理,在await之前和await之后,已经是两个不一样的线程ID。
也就是说,一个方法内部被拆分成了多个部分,不同的部分分别由不同的线程处理。
有返回值的异步调用
class Program
{
static void Main(string[] args)
{
Console.WriteLine("{0}->Main.异步方法执行前", Thread.CurrentThread.ManagedThreadId.ToString());//输出异步处理之前的线程ID
var asyncTask = DoAsync();//异步执行处理
Console.WriteLine("{0}->Main.异步方法执行后", Thread.CurrentThread.ManagedThreadId.ToString());//输出异步处理之后的线程ID
var result = asyncTask.Result;//等待异步处理完成才继续
Console.WriteLine("{0}->Main.异步方法完成后", Thread.CurrentThread.ManagedThreadId.ToString());//输出异步完成之后的线程ID Console.Read();
} /// <summary>
/// 执行异步处理
/// </summary>
/// <param name="times">模拟处理时长</param>
/// <returns></returns>
public static async Task<int> DoAsync(int times)
{
Console.WriteLine("{0}->DoAsync.await之前", Thread.CurrentThread.ManagedThreadId.ToString());//输出调用线程ID
var result = await Task.Run(() => { Thread.Sleep(times); return times; });///执行一个异步任务,并等待返回结果才继续;需要注意的是,调用线程执行到这一行的时候就已经返回了
Console.WriteLine("{0}->DoAsync.await之后", Thread.CurrentThread.ManagedThreadId.ToString());//异步操作执行完了,但这里已经是新的线程了
return result;//返回计算结果,注意:返回结果时和进入方法时的线程已经不一样了
}
}
->Main.异步方法执行前
->DoAsync.await之前
->Main.异步方法执行后
->DoAsync.await之后
->Main.异步方法完成后
请注意:在同步方法Main中执行的时候都是同一个线程;在异步方法DoAsync执行的时候,在await之前是调用线程,在await之后则是另一个线程。
总结
在异步(async)方法执行中,会根据await关键字,拆分一个方法为多个部分,分别由不同的线程执行。
在异步(async)方法执行中,遇到await关键字,调用线程会立即返回(线程池)继续后续的处理逻辑;而后,调用方可以使用Task.Wait()或Task<T>.Result进行阻塞,等待异步方法执行完毕再继续。
在异步(async)方法执行后,若不使用Task.Wait()进行等待,或不使用Task<T>.Result获取返回结果,则该方法将仅以异步方式执行。
那么异步方法的好处到底在哪?一下子,我也说不上来。。因为好像只用Task,也可以达到类似的效果,虽然也有区别。
难道,异步只是一颗语法糖吗?规范了异步方法的写法,回调函数也“消失”了。
其中异步方法最令人意外的,大概就是调用线程遇到await关键字,不用等待方法执行完毕就已经立即返回了吧!
同步夹杂着异步,异步夹杂着任务,异步方法里再拆分线程处理。想用好异步不容易!
参考
C#中的异步编程--探索await与async关键字的奥妙之处,原来理解和使用异步编程可以这么简单的更多相关文章
- C#异步编程のawait和async关键字来写异步程序
一.await和async关键字 .Net平台不断推出了新的异步编程模型,在.net4.5中加入了关键字await和async,顾名思义,await是指方法执行可等待,即可挂起直到有结果(不是必须立即 ...
- await和async关键字来写异步程序
await和async关键字出现于.Net5.0,方便写异步程序. 例子: public class MyClass { public MyClass() { DisplayValue(); //这里 ...
- C#~异步编程再续~await与async引起的w3wp.exe崩溃
返回目录 最近怪事又开始发生了,IIS的应用程序池无做挂掉,都指向同一个矛头,async,threadPool,Task,还有一个System.NullReferenceException,所以这些都 ...
- C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决
返回目录 关于死锁的原因 理解该死锁的原因在于理解await 处理contexts的方式,默认的,当一个未完成的Task 被await的时候,当前的上下文将在该Task完成的时候重新获得并继续执行剩余 ...
- C#中await和async关键字的简单理解
C# 5.0之后,为了简化异步编程,引入了异步函数的概念,也就是方法标记async,然后可以使用await表达式来等待异步操作返回. await关键字看起来是一个阻塞线程的调用,但是实际上执行到awa ...
- C#多线程编程(2)-- async,await基本用法
上一章我简单介绍了异步编程的基本方法,推荐使用的方式是Task.Task是对线程池的封装,并且可以对Task使用async和await关键字.这两个关键字的使用非常简单,那么这两个关键字究竟起什么作用 ...
- 小心C# 5.0 中的await and async模式造成的死锁
平时在使用C# 5.0中的await and async关键字的时候总是没注意,直到今天在调试一个ASP.NET项目时,发现在调用一个声明为async的方法后,程序老是莫名其妙的被卡住,就算声明为as ...
- 异步编程,await async入门
网上很多异步编程的文章,提供一篇入门: 异步编程模型 .net支持3种异步编程模式: msdn:https://docs.microsoft.com/zh-cn/dotnet/standard/asy ...
- C#进阶——从应用上理解异步编程的作用(async / await)
欢迎来到学习摆脱又加深内卷篇 下面是学习异步编程的应用 1.首先,我们建一个winfrom的项目,界面如下: 2.然后先写一个耗时函数: /// <summary> /// 耗时工作 // ...
随机推荐
- jdk8中CompletableFuture的各个API用法,极大扩展了Future
就不介绍了,直接贴代码,建议在代码中使用,真的很方便 package cn.hou.completablefuture; import org.junit.Test; import java.util ...
- Docker快速上手之搭建SpringBoot项目
Docker是基于Go语言实现的云开源项目. Docker的主要目标是“Build,Ship and Run Any App,Anywhere”,也就是通过对应用组件的封装.分发.部署.运行等生命周期 ...
- 学习记录(Python列表)
列表(List)是Python语言中最通用的序列数据结构之一,列表是一个没有固定长度的,用来表示任意类型对象的位置相关的有序集合.列表中的数据项不需要具有相同的数据类型 列表的基本操作: 1.创建列表 ...
- 如何播放 WAV 文件?
from http://www.vckbase.com/index.php/wv/434 平时,你在多媒体软件的设计中是怎样处理声音文件的呢?使用Windows 提供的API函数 sndPlaySou ...
- ps-如何移动照片里的内容
1.我们用内容感知移动工具把所要移动的区域大致勾选出来. 2.然后我们将所选区域拖动(点击鼠标左键不放拖动). 3.松开鼠标 4.仿制图章工具,alt 键取样,涂抹
- 设计模式——Adapter Pattern 适配器模式
我第一次接触设计模式,选取了四大类型里面的结构型,这类型的特点是关注类&对象之间的组合(使用继承),我从中选取适配器模式来具体学习. 一.适配器模式(Adapter Pattern)定义: 适 ...
- 2020-02-20Linux学习日记,第二天
在内容开始前请教一下博客园的大佬.编辑器中没有看到格式刷,要怎么不连续的选中内容给予想要的格式,有看到的麻烦私信解答一下,谢谢! ----------------------------------- ...
- c#设计模式读书博客
第一次在博客园撸博客,也是为了鞭策自己去学习进步,过年之后买了一本<C#设计模式>这是我一直很想去学习的一本书.然后用博客记录我的学习历程,并且分享给需要的人.这本书记录的设计模式有23种 ...
- 《C# 爬虫 破境之道》:第二境 爬虫应用 — 第七节:并发控制与策略
我们在第五节中提到一个问题,任务队列增长速度太快,与之对应的采集.分析.处理速度远远跟不上,造成内存快速增长,带宽占用过高,CPU使用率过高,这样是极度有害系统健康的. 我们在开发采集程序的时候,总是 ...
- django后台处理前端上传和显示图片
1:项目根目录存放图片的目录 2:settings.py 添加 MEDIA_ROOT = os.path.join(BASE_DIR, "media") 3:url.py 添 ...