这几天终于弄懂了async和await的模式,也搞明白了一直在心里面积压着的许多问题,所以写一篇博客来和大家分享一下。
关于异步机制我认为只要记住的以下几点,就可以弄明白了:
1.我认为async和awwait两个修饰符中最关键的是await,async是由于方法中包含await修饰符之后才在方法定义中添加的,表明这个方法是一个异步方法。
2.await只能用来修饰Task、Task<TResult>、ValueTask 或 ValueTask<TResult>这些类型的变量或者方法,他是一个分裂符,我们需要记住的最关键一点是当程序进行到await的时候,方法会暂时返回,而await字符之后的内容会等await这一行返回后继续执行。
3.在控制台应用中,await之前的内容是一个线程执行,await以及awiat之后的内容会在另一个线程中执行。
我们在编程时很多时候其实都是单行写的await,尤其是在主方法和mvc的控制器中,这也导致我一开始学习的时候对await产生了非常多的疑问,以至于一直都没有弄清楚,比如:
int bytesLoaded = await DownloadDocsMainPageAsync();
这个几乎是我们写代码时见到最多的用法了,在主方法中使用await 来做一些request的请求,当时就在想await不是异步吗,但为什么还要在这里等待这个输出才进行下一步的执行呢?那么异步是异步到哪里了呢?其实这种使用方式并没有体现出异步的特色,只是因为request中很多方法是异步方法而为了获取结果才这么写的,便于理解的方式其实是下面这样的:
Task<int> downloading = DownloadDocsMainPageAsync(); //省略代码,这里有一些处理方式// int bytesLoaded = await downloading;
关于如何使用,感谢评论中的同学指出,其实并不是上面的方式就是正确的使用方式,之前的写法太主观了,这种写法我觉得更便于我们理解。await之前都是同一个线程执行的,所以多行写还是单行写无所谓效率问题,如果是嵌套在方法中使用,单行写法可能更准确一些(更能表达出在执行这个方法时开始异步)。
异步是在我们处理一件事的时候可以同时进行着另一件事,下图说明了上面代码在主方法中的调用逻辑:
另外我写了一个异步的demo,这个方法里面有两个异步的例子,大家有兴趣的话可以在本地自己调试去感受一下异步的实现方式和调用的顺序,方便大家理解:
//private static void Main(string[] args) //{ // //Console.WriteLine("111 balabala. My Thread ID is :" + Thread.CurrentThread.ManagedThreadId); // //var r = AsyncMethod(); // //Console.WriteLine(r); // //Console.WriteLine("222 balabala. My Thread ID is :" + Thread.CurrentThread.ManagedThreadId); // //Thread.Sleep(10000); //} public static async Task Main() { Task<int> downloading = DownloadDocsMainPageAsync(); Console.WriteLine($"{nameof(Main)}: Launched downloading."); Console.WriteLine("main1 " + Thread.CurrentThread.ManagedThreadId); int bytesLoaded = await downloading; Console.WriteLine("main2 " + Thread.CurrentThread.ManagedThreadId); Console.WriteLine($"{nameof(Main)}: Downloaded {bytesLoaded} bytes."); } private static async Task<int> DownloadDocsMainPageAsync() { Console.WriteLine($"{nameof(DownloadDocsMainPageAsync)}: About to start downloading."); Console.WriteLine("sub1 " + Thread.CurrentThread.ManagedThreadId); var client = new HttpClient(); byte[] content = await client.GetByteArrayAsync("https://docs.microsoft.com/en-us/"); Console.WriteLine("sub2 " + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(5000); Console.WriteLine($"{nameof(DownloadDocsMainPageAsync)}: Finished downloading."); return content.Length; } private async static Task<String> AsyncMethod() { var ResultFromTimeConsumingMethod = TimeConsumingMethod(); string Result = await ResultFromTimeConsumingMethod + " + AsyncMethod. My Thread ID is :" + Thread.CurrentThread.ManagedThreadId; Console.WriteLine(Result); Console.WriteLine(Thread.CurrentThread.ManagedThreadId); return Result; //返回值是Task的函数可以不用return } //这个函数就是一个耗时函数,可能是IO操作,也可能是cpu密集型工作。 private static Task<string> TimeConsumingMethod() { var task = Task.Run(() => { Console.WriteLine("Helo I am TimeConsumingMethod. My Thread ID is :" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(5000); Console.WriteLine("Helo I am TimeConsumingMethod after Sleep(5000). My Thread ID is :" + Thread.CurrentThread.ManagedThreadId); return "Hello I am TimeConsumingMethod"; }); return task; }
最后附上我参考的一些内容:
https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/await
上面demo有一个被注释了的main方法是我参考的另一个大神的博客,正是读了他的博客才让我恍然大悟(虽然里面的其他叙述也不是非常准确),但是在理解async和await方面,这篇博客真正点醒了我,很可惜我当时读博客的时候并没有收藏,后面也没有找到博客地址,但是还是要在这里感谢他。