先看这样一段常规代码:(假设两个耗时的方法为文件或数据库或网络请求操作)
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var result = CallMethod();
Console.WriteLine($"After method, Total: {result}");
Console.ReadKey();
}
public static int CallMethod()
{
var a = Method1();
var b = Method2();
Console.WriteLine($"Result:{a}, {b}");
return a + b;
}
public static int Method1()
{
Console.WriteLine("Method1 start.");
Thread.Sleep(10000);
Console.WriteLine("Method1 end in 10s.");
return 1;
}
public static int Method2()
{
Console.WriteLine("Method2 start.");
Thread.Sleep(5000);
Console.WriteLine("Method2 end in 5s.");
return 2;
}
}
执行结果:
Hello World!
Method1 start.
Method1 end in 10s.
Method2 start.
Method2 end in 5s.
Result:1, 2
After method, Total: 3
两个子方法运行分别需要10秒和5秒,所以整个方法总共需要15秒时间。
接下来我们把两个子方法转换成异步方法:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var res = Task.Run(CallMethod).GetAwaiter().GetResult();
Console.WriteLine($"After method, Total: {res}");
Console.ReadKey();
}
public static async Task<int> CallMethod()
{
var a = await Method1();
var b = await Method2();
Console.WriteLine($"Result:{a}, {b}");
return a + b;
}
public static async Task<int> Method1()
{
var a = await Task.Run(() =>
{
Console.WriteLine("Method1 start.");
Thread.Sleep(10000);
Console.WriteLine("Method1 end in 10s.");
return 1;
});
return a;
}
public static async Task<int> Method2()
{
var a = await Task.Run(() =>
{
Console.WriteLine("Method2 start.");
Thread.Sleep(5000);
Console.WriteLine("Method2 end in 5s.");
return 2;
});
return a;
}
}
因为是在Console程序里,Main方法本身不允许加async参数,所以不能直接使用await,要使用GetResult方式获取异步结果。程序执行结果:
Hello World!
Method1 start.
Method1 end in 10s.
Method2 start.
Method2 end in 5s.
Result:1, 2
After method, Total: 3
嗯?执行结果和顺序一模一样,执行时间也一样,还是15秒。await一个异步方法,和执行一个同步方法,效果是完全一样的吗?
我们来修改一下CallMethod方法:
public static async Task<int> CallMethod()
{
var a = Method1();
var b = Method2();
var a1 = await a;
var b1 = await b;
Console.WriteLine($"Result:{a1}, {b1}");
return a1 + b1;
}
再执行一下,程序执行结果:
Hello World!
Method1 start.
Method2 start.
Method2 end in 5s.
Method1 end in 10s.
Result:1, 2
After method, Total: 3
OK,两个方法同时启动并行,时间短的先返回,时间长的后返回,但两个都返回以后,下面的代码才执行并返回。总执行时间变成了10秒。
唯一的区别是,把调用异步方法,和等待异步方法的结果,分成了两行来写。
这种情况下,如果是WebApi里面的某个接口,需要调用数据库执行5个不同的SQL语句(或者远程HTTP接口),将返回结果拼接起来返回给前端。如果每条SQL语句执行需要3秒(真是太慢了),那这个接口在顺序执行的情况下需要15秒才能返回,而在异步并行的情况下,总共只需要3秒。(前提是SQL数据库CPU没有受限)
接下来,假如CallMethod只需要执行几个异步任务,比如执行几个远程请求,再根据请求结果保存到数据库,并不需要返回结果,也就是void方法。把Main方法中调用的地方改成:
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Task.Run(CallMethod).Wait();
Console.WriteLine($"After method.");
Console.ReadKey();
}
再运行一次:
Hello World!
Method1 start.
Method2 start.
After method.
Method2 end in 5s.
Method1 end in 10s.
Result:1, 2
结果虽然我们在Main方法里要求它等待CallMethod的执行,但是因为它是void方法,程序认为等待它的结果没有意义,然后就跳过了等待,继续执行了后面的代码。然后另外两个方法在后台默默的完成。如果是在一个WebApi的接口里,那结果就是接口立即返回了。