等待立即执行和稍后执行的区别

10

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

因此,我一直这样做:

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

await调用后立即使用它,但最近我看到一些帖子中的人们正在做以下事情:

await调用之后等待并立即使用,但是我最近看到一些帖子中的人们会这样做:

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

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


5
这句话的意思是你最初的假设。 - J. Steen
1
许多人认为await开始某些事情。这是完全错误的。await只是等待某些事情完成——如何启动这件事完全取决于其他东西的实现细节——在这种情况下,取决于GetValueAsync的内部实现方式。 - Damien_The_Unbeliever
1
到目前为止,这三个答案都没有讨论生成的状态机,我认为这也是一个潜在重要的理解点。 - Colin Mackay
@ColinMackay 很好的观点。我记得生成能够包装异步方法调用的IL代码是一个有趣的麻烦,因为状态机的工作方式。 - J. Steen
当您想要运行多个任务时,启动它们全部,然后等待它们完成可以非常实用。 - Dennis_E
显示剩余4条评论
5个回答

6
达明_不信神在他说它取决于实现时是正确的。
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

DoSomethingAsyncB需要5秒钟和10个参数。 - Jamie Rees
那么 var result = await GetValueAsync(); DoSomethingElse(); 呢?如果我理解正确,DoSomethingElse() 将在 GetValueAsync() 完成后被调用,然而我几乎在任何地方都看到这种编码风格,让我想知道异步等待范式的用途是什么。 - wensveen

4

在这个

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 完成时调用 DoSomethingElse,然后当它完成后继续执行 DoAnotherThing。


2
很多人认为异步等待是由几个线程完成的。通常情况下并非如此,除非你启动其他线程,否则所有代码都由同一个线程执行。检查当前线程的ThreadId可以证明这一点。
在一次Eric Lippert的采访中,他用餐厅隐喻解释了异步等待。在中间某个地方搜索异步等待。
他解释说,如果一个厨师烤面包,他可以等待面包烤好,或者开始做其他事情,稍后回来看看烤面包机是否已经完成了烤制。
在您的第二个DoSomethingAsync中也是如此。当将数据写入磁盘时,您的线程会将数据交给磁盘写入器。将数据写入磁盘是一个相对较慢的过程。线程不会等待数据被写入,而是开始做其他事情。当它完成其他事情后,线程会等待磁盘写入完成。
这只有在磁盘写入可以在您的线程执行其他操作时继续写入时才有用。因此,通常在涉及硬件的情况下或启动其他线程来执行某些工作的进程中看到异步函数。如果异步函数需要进行一些耗时的计算,则将函数设置为异步不是有用的,除非它启动另一个线程来执行计算。即使如此,是否应该在异步函数内部执行此操作还是调用方应该有自由决定让自己的线程执行计算或启动不同的线程来执行此操作仍然是有问题的。 Stephen Cleary 写的另一篇文章帮助我理解异步等待的原理。他是这个网站的杰出贡献者(再次感谢 Stephen!)

好的例子。谢谢。 - Jamie Rees

1
在第一个示例中,在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"

    }

0

你只需将任务结果的要求推迟到实际首次使用它的语句中,这意味着在执行到该语句时,任务可能已经异步设置了其结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接