我做了一个有趣的观察,我想完全理解。
最简单的解释方法是使用这个小样本控制台应用程序捕获它:
namespace AsyncAwaitTestApp
{
using System;
using System.Diagnostics;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
var timer = new Stopwatch();
// First Run:
// -------------------------
Console.WriteLine("Running GiveMeATask in parallel...");
timer.Start();
Parallel.For(0, 500, (i) =>
{
SillyClass.GiveMeATask().Wait();
});
timer.Stop();
Console.WriteLine($"GiveMeATask run completed. Total time: {timer.Elapsed.TotalSeconds} seconds.");
// Second Run:
// -------------------------
Console.WriteLine("-------------------------------");
Console.WriteLine("Running AwaitATask in parallel...");
timer.Restart();
Parallel.For(0, 500, (i) =>
{
SillyClass.AwaitATask().Wait();
});
timer.Stop();
Console.WriteLine($"AwaitATask run completed. Total time: {timer.Elapsed.TotalSeconds} seconds.");
Console.ReadLine();
}
}
public class SillyClass
{
public static TimeSpan SomeTime = TimeSpan.FromSeconds(3);
public static Task GiveMeATask()
{
return Task.Delay(SomeTime);
}
public static async Task AwaitATask()
{
await Task.Delay(SomeTime);
}
}
}
它也可以Gist。
SillyClass
有两种方法都返回Task
。在这两种方法中,我有3秒的Task.Delay
来模拟昂贵的网络或IO绑定操作。
第一种方法GiveMeATask
只是从Task.Delay
返回任务。第二种方法AwaitATask
等待Task.Delay
和async
/ await
个关键字。
从Parallel.For
调用这两个方法来模拟并发调用。
我觉得有趣的是,无论我重复多少次,第一次运行总是花费大约两倍的时间,而不是重复次数:
据我所知,async
/ await
模式必须捕获同步上下文,这会增加一些开销,但如果这是原因我会感到惊讶吗?
我也理解在某些应用程序中,例如WPF应用程序或只有1个线程可以访问UI / Request上下文的ASP.NET应用程序,同步阻塞.Wait()
会导致死锁,但事实并非如此对于控制台应用程序,显然我没有死锁,因为两个运行完全正常。
所以我显然不理解上述情况的全貌,并希望有人能够对此有所了解?