我有两个API,它们调用相同的服务。
此版本的执行时间是最昂贵的任务。
public async Task<double> Do()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var t1 = _service.Do1();
var t2 = _service.Do2();
await t1;
await t2;
return sw.Elapsed.TotalMilliseconds;
}
这是每次任务延迟总和。
public async Task<double> Do()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
await _service.Do1();
await _service.Do2();
return sw.Elapsed.TotalMilliseconds;
}
internal async Task Do1() => await Task.Delay(5000);
internal async Task Do2() => await Task.Delay(2000);
为什么会这样,实际上是什么情况?
答案 0 :(得分:6)
任务返回“ hot”,或者已经开始。 await
关键字的字面意思是在此处保留并等待任务完成。这样,当您分别等待每个操作时,它将调用Do1
,等待操作完成,然后调用Do2
并等待操作完成。
但是,当您仅将它们存储在变量中时,它将在Do1
仍在运行时先调用Do2
,然后再调用Do1
。稍后,当您await
这两个变量时,代码将等待每个变量实际完成,但是它们已经在运行。
长短不一,这是串行运行和并行运行之间的区别。异步操作 可以并行运行,但本质上不是并行的:这是两个不同的概念。
答案 1 :(得分:3)
var t1 = _service.Do1(); < --- your 1st task started
var t2 = _service.Do2(); < --- your 2nd task started
await t1; < --- 1st awaited but it already did at least part of its job so far
await t2; < --- 2nd task awaited but, if its light task, probably already done so nothing to await
vs
await _service.Do1(); < --- 1st task started and blocks 2nd from starting
await _service.Do2(); < --- 2nd starts only after 1st finished
很明显,案例1并行运行了一段时间,而案例1并行运行,因此将这两个任务的全部时间加起来。
答案 2 :(得分:3)
在第一种情况下,两个任务都将在您等待其中一个任务之前启动。因此,您只需要时间max(time(task1), time(task2))
。
在第二种情况下,您启动第一个任务,等待它完成,然后再启动第二个任务,因此您的时间将为time(task1) + time(task2)
编辑: 另外请注意受CPU限制的任务,因为如果在同一线程上执行任务,这些任务将不会自动并行运行,例如
public static async Task<double> Do()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var t1 = Do1();
var t2 = Do2();
await t1;
await t2;
var time = sw.Elapsed.TotalMilliseconds;
Console.WriteLine("time="+time); // time=3002.3204
return time;
}
public static async Task Do1()
{
var t = Task.Delay(1000);
while(t.Status != TaskStatus.RanToCompletion) {}
await t;
}
public static async Task Do2()
{
var t = Task.Delay(2000);
while(t.Status != TaskStatus.RanToCompletion) {}
await t;
}