我们大家都知道Task.Delay是异步等待,Thread.Sleep是同步等待,那到底同步等待和异步等待有什么区别呢?
下面我们用例子来分析一下,新建一个控制台应用程序,代码如下:
static async Task Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
var s = await MyTask();
Console.WriteLine(s);
}
Console.ReadKey();
}
static async Task<string> MyTask()
{
var s = await Run();
Console.WriteLine("MyTask");
return s;
}
static async Task<string> Run()
{
await Task.Delay(5000);
Console.WriteLine("Run");
return await Task.FromResult("Run");
}
运行结果如下:
这里只能看到最终的运行结果,看不到执行过程。执行过程是:程序运行起来后,过5秒钟,打印出Run,然后紧接着打印MyTask和Run,然后再这样循环4次。下面我们把Run方法里面的Task.Delay换成Thread.Sleep,发现执行过程是一样的。这里我就不写了,大家可以自己试一下。
那是不是说Task.Delay和Thread.Sleep没什么区别呢,答案肯定是否定的。因为我们这里用了2个await,await的作用就是等待任务完成,不信的话,我们把Run方法里面的Task.Delay(5000)的await去掉,再运行一下程序,发现程序没有任何等待。
我们再把Run方法里面的Task.Delay替换成Thread.Sleep,发现程序还是会等待。因为Thread.Sleep是同步等待,不管程序运行在哪里,只要遇到Thread.Sleep就会阻塞当前线程。
其实我感觉上面的例子不能很好的表达我的观点,下面我们再看一下例子:
static async Task Main(string[] args)
{
Run2();
Console.WriteLine("after run2");
Console.ReadKey();
await Task.CompletedTask;
}
static async Task<string> Run2()
{
await Task.Delay(5000);
Console.WriteLine("Run2");
return await Task.FromResult("Run2");
}
这个例子的输出顺序是:先输出after run2,再输出Run2。我们再看一下编译产生的警告:
这个警告是因为Run2是一个异步方法,但是在Main函数中调用的时候,没有用await来等待异步方法,所以程序不会等待该异步方法执行完毕,然后再往下执行。因为程序没有等待,所以先输出了after run2。
我们再把Run2方法中的await换成Thread.Sleep,那就是先输出Run2了。
当我们使用Delay方法时,前面一定要加await,这个道理就和Run2方法前面要加await一样,因为Delay方法也是异步的。
我们建议使用await Task.Delay来执行等待操作,因为它是异步的,关于异步的原理,推荐大家看一篇微软官方的文档:
https://docs.microsoft.com/zh-cn/dotnet/standard/async-in-depth