具有不同执行时间的异步流

时间:2019-08-02 16:35:01

标签: c# .net asynchronous async-await

我有两个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);

为什么会这样,实际上是什么情况?

3 个答案:

答案 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;
}

Try it online!