直接等待和稍后等待的区别

本文关键字:等待 区别 | 更新日期: 2023-09-27 17:57:54

我找不到任何关于实际差异的信息。

所以我一直在做以下事情:

public async Task<int> DoSomethingAsync()
{
    DoSomethingElse()
    var result = await GetValueAsync();
    var intValue = DoAnotherThing(result);
    return intValue;
}

await调用,然后直接使用它,但我最近看到一些帖子,人们正在做以下事情:

public async Task<int> DoSomethingAsync()
{    
    var task = GetValueAsync();
    DoSomethingElse()
    var result = await task;
    var intValue = DoAnotherThing(result);
    return intValue;
}

那么,这是否意味着方法GetValueAsync开始执行它,然后我们稍后等待它的结果?或者这意味着它正在同步执行,但它仍然可以await

直接等待和稍后等待的区别

Damien_The_Unbeliever说得对,这取决于实现。

class Program
{
    public static int DoAnotherThing(string value)
    {
        return int.Parse(value);
    }
    public static async Task<string> GetValueAsync()
    {
        await Task.Delay(5000);
        return "12";
    }
    public static void DoSomethingElse()
    {
        Thread.Sleep(5000);
    }
    public static async Task<int> DoSomethingAsyncA()
    {
        DoSomethingElse();
        var result = await GetValueAsync();
        var intValue = DoAnotherThing(result);
        return intValue;
    }
    public static async Task<int> DoSomethingAsyncB()
    {
        var task = GetValueAsync();
        DoSomethingElse();
        var result = await task;
        var intValue = DoAnotherThing(result);
        return intValue;
    }
    public static void Measure(Action act)
    {
        var sw = new Stopwatch();
        sw.Start();
        act();
        sw.Stop();
        Console.WriteLine(sw.Elapsed);
    }
    static void Main(string[] args)
    {
        Measure(() => DoSomethingAsyncA().Wait());
        Measure(() => DoSomethingAsyncB().Wait());
        Console.ReadLine();
    }
}

在这种情况下,DoSomethingAsyncB将花费约5秒,其中DoSomethingAsyncA将花费约10秒。但是,如果GetValueAsync返回的值计算是从其他地方开始的,那么就没有区别了。

更新我更正了答案中的错误。这是我机器的输出:

  00:00:10:0161446
  00:00:05:0032000

在此中

public async Task<int> DoSomethingAsync()
{
    DoSomethingElse()
    var result = await GetValueAsync();
    var intValue = DoAnotherThing(result);
    return intValue;
}

这意味着当您在不执行任何其他代码的情况下尽快等待GetValueAsync的结果时。

下面的代码表示:

public async Task<int> DoSomethingAsync()
{    
    var task = GetValueAsync();
    DoSomethingElse()
    var result = await task;
    var intValue = DoAnotherThing(result);
    return intValue;
}

在等待GetValueAsync完成时,您调用GetValueAsyncanddoSomethingElse,然后当它完成时,它继续执行DoAnotherThing

人们通常认为异步等待是由几个线程完成的。通常情况并非如此。除非启动其他线程,否则所有代码都由同一个线程执行。检查当前线程的ThreadId会显示这一点。

在一次采访中,Eric Lippert用餐馆的比喻解释了异步等待。在中间的某个位置搜索异步等待。

他解释说,如果厨师烤了一些面包,他可以等到面包烤好,或者开始做其他事情,然后再回来看看烤面包机是否烤好了。

在您的第二个DoSomethingAsync中也会发生同样的情况。将日期写入磁盘时,线程会将数据传递给磁盘写入程序。写入磁盘是一个相对缓慢的过程。线程不等待写入日期,而是开始做其他事情。当它完成其他操作时,线程将等待,直到磁盘写入完成对磁盘的写入。

只有当磁盘写入可以在线程执行其他操作时继续写入时,这才有用。因此,当涉及硬件时,您通常会看到异步函数,或者启动其他线程来完成某些工作的进程。如果异步函数必须进行一些耗时的计算,那么使函数异步是没有用的,除非它启动另一个线程来进行计算。即便如此,这是否应该在异步函数中完成,或者调用方是否应该自由决定是让自己的线程进行计算,还是启动另一个线程来进行计算,都是值得怀疑的。

另一篇帮助我理解异步等待的文章由Stephen Cleary撰写,他是本网站的伟大贡献者(再次感谢Stephen!)

在第一个例子中,在GetValueAsync()返回结果之前,您不会执行代码。

在第二个步骤中,您将执行var task = GetValueAsync();var result = await task;之间的代码,执行DoSomethingElse()

参见小示例:

    public static Task<int> DoLongOperationAsync()
    {
        return Task.Run(() => { Thread.Sleep(5000); return 5; }); // 5 seconds
    }

    public static async Task TestMethod()
    {
        var result = await DoLongOperationAsync();
        Console.WriteLine("Console");
        Console.WriteLine(result);
    }
    public static async Task TestMethod2()
    {
        var result = DoLongOperationAsync();
        Console.WriteLine("Console");
        Console.WriteLine(await result);
    }

    static async DoSomething()
    {
        await TestMethod(); // here you see some text in console only after 5 seconds
        await TestMethod2(); // here you see some text in console immediately and after 5 seconds you will see "5"
    }

您只需将Task结果的要求推迟到第一次实际使用它的语句,这意味着当执行到达该语句时,任务可能已经异步设置了它的结果。