Async/Await 已经出来许多年,虽然生前是个老码农,但对C#依然是新手,所以在学习的操作过程中做个历史记录,算第二份把。
为什么须要Async/Await?
为什么须要Async/Await?因为触发器程式设计须要啊!
触发器程式设计不是早有了吗,为什么须要Async/Await?没Async/Await,你总之能用现代手段实现触发器程式设计,建立两个thread,在把干的表达式传达到thread,接着外边继续干别的活,外面干的相差无几了,就去等thread的结果,接着处理意见,搽鼻子….,这叮当下来,几百行标识符列先卡了,除此以外,你自己写的现代形式的标识符还可能将出现各种问题(比如说鼻子没擦干净)。(Chavanges:写了多年的C,表示很难过)
有了Async/Await,你只须要录于标识符就可搞掂,而且更安全,更放心。不香吗?不香吗!
有些老师对触发器程式设计可能将没啥概念,这里举个范例:你在写两个GUI的应用流程,那个流程须要读取两个特别大的文档,那个操作过程可能将须要30s:
假如是并行程式设计商业模式,在读取文档的这30s里头,使用者是不能进行任何人操作形式的,比如说点个按键什么的,换做你,你会不会认为流程是不是挂了(鬼知道文档读取的好不好了?总之smart一点你能搞两个bar来显示,但也须要触发器哦)
假如是触发器程式设计商业模式,你能改到“在前台”读取那个文档,主旋律程依然能serve使用者的操作形式,比如说点个按键啥的,假如使用者要操作形式文档,你能很nice的告诉用户文档正在读取中,读取多少了等等,明显那个使用者新体验较佳。
Async/Await的突破点归纳
先上干,把突破点归纳一下,搞不懂说实话,直接埃唐佩县方可。或许把突破点放前面是因为标识符太长,不想把重点拉到太前面了。
Async是C#的缩排,能用以润色method或是expression,用以新闻稿两个触发器的method或是expression两个触发器的method仅全力支持以下codice:TaskTask<TResult> // C#void // 表述void回到实际上是为了跟一般表达式保持相容任何人包涵可出访GetAwaiter()形式的类型(从C# 7.0已经开始全力支持)Async形式在继续执行的时候,已经开始是以并行的形式执行(即在初始化方的thread里跑的),直到遇到awaitURL,从awaitURL已经开始,C#会另起两个thread来继续执行await前面的标识符。假如Async形式里头的标识符没包涵await的标识符会怎么样?那整个表达式就会并行继续执行,跟一般表达式没差异。C++也会来点警示。话不多说,上标识符
using System;
using System.Threading;
public class AsyncAwaitTest
{
static void Main(string[] args)
{
Console.WriteLine(“Main start, Thread ID is ” + Thread.CurrentThread.ManagedThreadId);
var teaResult = PrepareteaAsync();
var hotWaterResult = PrepareHotWaterAsync();
var cupResult = PrepareCupAsync();
Console.WriteLine(“Main waiting, Thread ID is ” + Thread.CurrentThread.ManagedThreadId);
teaResult.GetAwaiter().GetResult();
hotWaterResult.GetAwaiter().GetResult();
cupResult.GetAwaiter().GetResult();
Console.WriteLine($”All Done! tea={teaResult.Status}, hotWater={hotWaterResult.Status}, cup={cupResult.Status}, Thread ID is ” + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(“Main end, Thread ID is ” + Thread.CurrentThread.ManagedThreadId);
}
private static async Task PrepareteaAsync()
{
Console.WriteLine(“PrepareteaAsync start, Thread ID is ” + Thread.CurrentThread.ManagedThreadId);
await TimeConsumingMethod(3000);
Console.WriteLine(“PrepareteaAsync end, Thread ID is ” + Thread.CurrentThread.ManagedThreadId);
}
private static async Task PrepareHotWaterAsync()
{
Console.WriteLine(“PrepareHotWaterAsync start, Thread ID is ” + Thread.CurrentThread.ManagedThreadId);
await TimeConsumingMethod(5000);
Console.WriteLine(“PrepareHotWaterAsync end, Thread ID is ” + Thread.CurrentThread.ManagedThreadId);
}
private static async Task PrepareCupAsync()
{
Console.WriteLine(“PrepareCupAsync start, Thread ID is ” + Thread.CurrentThread.ManagedThreadId);
await TimeConsumingMethod(2000);
Console.WriteLine(“PrepareCupAsync end, Thread ID is ” + Thread.CurrentThread.ManagedThreadId);
}
static Task<int> TimeConsumingMethod(int timeCost)
{
var task = Task.Run(() => {
Console.WriteLine($”TimeconsumingMethod({timeCost}) start with Thread ID is {Thread.CurrentThread.ManagedThreadId}”);
Thread.Sleep(timeCost);
Console.WriteLine($”TimeconsumingMethod({timeCost}) end with Thread ID is {Thread.CurrentThread.ManagedThreadId}”);
return timeCost;
});
return task;
}
}
运行结果如下:
Main start, Thread ID is 1
PrepareteaAsync start, Thread ID is 1
PrepareHotWaterAsync start, Thread ID is 1
PrepareCupAsync start, Thread ID is 1
Main waiting, Thread ID is 1
TimeconsumingMethod(3000) start with Thread ID is 4
TimeconsumingMethod(2000) start with Thread ID is 7
TimeconsumingMethod(5000) start with Thread ID is 6
TimeconsumingMethod(2000) end with Thread ID is 7
PrepareCupAsync end, Thread ID is 7
TimeconsumingMethod(3000) end with Thread ID is 4
PrepareteaAsync end, Thread ID is 4
TimeconsumingMethod(5000) end with Thread ID is 6
PrepareHotWaterAsync end, Thread ID is 6
All Done! tea=RanToCompletion, hotWater=RanToCompletion, cup=RanToCompletion, Thread ID is 1
Main end, Thread ID is 1
标识符解析
上面用泡茶的范例来解析,简单拆解为3个步骤:准备茶叶,烧已经开始,洗杯子,三个步骤在Main表达式都是并行的,最后等待三者的结果能看到PrepareteaAsync(), PrepareHotWaterAsync(), PrepareCupAsync() 三个表达式的第一行log都是在thread ID为1的进程打印的,而前面的部分都是在各自的子线程里打印出来的。理解这点很重要。在三个触发器线程启动完成后,我们还须要等待回到结果。假如那个时候主旋律程退出的话,那子线程也会随之退出的。在刚已经开始写Console Application的测试code我就遇到那个问题,刚已经开始还加sleep来等,或是干脆写个ReadKey()等,其实用GetAwaiter()才是正解。每个Async形式的回到其实是个Task,关于Task的更多属性能参考前面的链接的内容。参考链接:
Asynchronous programming in C# | Microsoft Docs
The Task Asynchronous Programming (TAP) model with async and await (C#)” | Microsoft Docs
Async and Await In C# (c-sharpcorner.com)
Understanding async / await in C# – Stack Overflow
Async and Await (stephencleary.com)