c#的async和await
调用和执行
C#5.0的引入了async和await两个修饰符,是异步编程的核心关键字,其中async 修饰后表示这个方法是可异步的,相当于贴上了一个异步标签;await顾名思义“等待”,表示被调方法内部在执行完await之前将会被阻塞。
这里涉及到的两个方法,简单的说就是,被调方法内部阻塞,主调方法异步(并行),用以下例子进行分析:
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
internal class Program
{
static void Main(string[] args)
{
DoSth();
Console.WriteLine("wait1...");
Console.ReadLine();
}
static async Task DoSth()
{
var task = await Loading();
Console.WriteLine("wait2...");
Console.WriteLine($"load end {task}");
}
static async Task<string> Loading()
{
Console.WriteLine("load 3 seconds");
await Task.Delay(3000);
return "ok";
}
}
}
- dosth和loading,两个方法都被async修饰,表示是异步方法。
- dosth中调用loading,而且是await方式,此时有两层含义,一是表示该方法(dosth)可以以异步方式“被”调用,二是dosth方法内部将会阻塞,直到loading执行完,方法内部才会继续往下执行“Console.WriteLine wait2…”。
- dosth的调用方main是同步调用的,main内部不会阻塞,dosth和“Console.WriteLine wait1”将会并行执行。
- 需要注意的是,loading方法中的代码并不全是异步的,你会发现在调用执行到await关键字之前的代码都是同步方式顺序执行的,所以会首先输出"load 3 seconds"。
以上例子在IDE中main方法中的DoSth()被绿色下划线提示“由于此调用不会等待,因此在调用完成之前,将继续执行当前方法”,此时可将DoSth返回值改为void,因为awiat不会等待void,此时方法限制为只能同步调用。
static async void DoSth()
{
var task = await Loading();
Console.WriteLine("wait2...");
Console.WriteLine($"load end {task}");
}
执行结果:
Task.run
Task.run是后台线程池执行,可以保护主线程不受耗时任务影响,例如下载文件,远程耗时请求等。
需注意异步方法的执行不一定是会以起线程的方式执行,这里有任务和状态机的概念,感兴趣的童鞋可以深入研究,而Task.run的存在使我们可以根据自己的业务复杂程度灵活指定线程执行。
Thread.sleep Or Task.delay
Task方法内部需要sleep时不能用thread.sleep 而是用task.delay,原因是例如在进行一些background线程执行一些初始化或者心跳时,会将这些方法的调用线程阻塞,导致异常情况出现,task.delay不阻塞线程,仅此而已。