【问题标题】:What makes these async method calls different?是什么让这些异步方法调用不同?
【发布时间】:2018-12-15 18:29:21
【问题描述】:

我正在阅读我的 C# 书中关于 await 和 async 关键字的一章。 主要讲解这几种方法调用,调用者使用await关键字等待被调用方法完成。

在这个简单的示例中,我没有看到任何优势,但更重要的是,这 3 个调用没有区别。谁能解释它对应用程序流程有什么影响?只有当调用线程是主GUI线程时才有用吗?

static async Task Main(string[] args)
{
    WriteText();

    await WriteTextAsync();

    WriteTextAsync().Wait();
}

static void WriteText()
{
    Thread.Sleep(3_000);
    Console.WriteLine("Hello");
}

static async Task WriteTextAsync()
{
    await Task.Run(() =>
    {
        Thread.Sleep(3_000);
        Console.WriteLine("Hello");
    });
}

Ps:如果方法的调用线程无论如何都在等待方法完成,那还不如正常调用?

【问题讨论】:

  • 那位作者懒得把线程 ID 作为Console.WriteLine 的一部分打印出来,因为这样你几乎可以立即看到差异。
  • @LexLi 除此之外,我认为仅在控制台中使用 Thread.Sleep 并不足以显示实际增加的优势。另外,用Task.Run调用Thread.Sleep?不是我最喜欢的例子……
  • @CamiloTerevinto 我同意。 async/await 从来都不是一个容易掌握的概念,糟糕的例子可能会产生误导。
  • 这只是一个坏例子。它可以教你一些关于“如何”的知识,但不能教你关于事情的“为什么”。继续阅读或寻找更好的来源。
  • 特别是你给定的例子不会得到sync-await的好处。

标签: c# asynchronous async-await


【解决方案1】:

我对你的问题的理解是

如果程序在 await WriteTextAsync() 行等待响应,那么会有什么好处?

对于客户端应用程序,例如 Windows 应用商店、Windows 桌面和 Windows Phone 应用程序,异步的主要好处是响应能力。这些类型的应用程序主要使用异步来保持 UI 响应。对于服务器应用程序,异步的主要好处是可扩展性。

我将尝试从 web-app 的角度进行解释。

假设您有一个 Web 应用程序依赖于外部资源(如数据库调用),当客户端发起请求时,ASP.NET 会获取其线程池线程之一并将其分配给该请求。因为它是同步编写的,所以请求处理程序将同步调用该外部资源。这会阻塞请求线程,直到对外部资源的调用返回。图 1 说明了一个具有两个线程的线程池,其中一个线程被阻塞等待外部资源。

图 1 同步等待外部资源

现在如果第三个客户端同时请求,那么线程池中没有线程可用于分配第三个请求。

在异步调用中,线程不会被卡住,而是会被释放,回到线程池,方便服务于第三次调用。

当请求服务器活动结束时,链接数据库调用结束,然后SynchronizationContext 恢复该调用并将休息返回给客户端。

下面是 aync 调用的简单类比

幕后发生了很多事情。我从Async Programming : Introduction to Async/Await on ASP.NET 和我的理解写了这封邮件。强烈建议在使用async-wait之前有清楚的了解。

【讨论】:

    【解决方案2】:

    我指的是:

    //Call 1
    WriteText();
    
    //Call 2    
    await WriteTextAsync();
    
    //Call 3
    WriteTextAsync().Wait();
    

    第一次调用没有任何问题,如果你想做的是同步等待。在控制台应用程序中,这是很正常的。

    问题出现在具有 UI 的程序或需要充分利用 CPU 资源的程序中,最常见的情况是 Web 应用程序。

    调用 2,使用 await 执行异步等待 WriteTextAsync 的结果。就其本身而言,这很好,这被认为是正常的。但是,WriteTextAsync 是一个很好的例子,说明你应该永远不要这样做:

    static async Task WriteTextAsync()
    {
        // Let's create a Thread
        await Task.Run(() =>
        {
            // just to block it completely, having it do nothing useful
            Thread.Sleep(3_000);
            Console.WriteLine("Hello");
        });
    }
    

    相反,作者应该使用:

    static async Task WriteTextAsync()
    {
        // Let's *not* create a new thread
        await Task.Delay(3_000);
        Console.WriteLine("Hello");
    }
    

    也许他们会进一步指出这一点,但你没有给我们书名来知道这一点。

    调用编号 3 是当调用方法不能是 async 并且您必须调用 async 方法时您必须执行的操作,因此:

    // Think that for some reason you cannot change the signature, 
    // like in the case of an interface, and an async void would make your code
    // never complete correctly
    static void Main(string[] args)
    {
        //Call 3
        WriteTextAsync().Wait();
    }
    

    总的来说,我建议你找一本更好的书。当实际需要异步代码时,示例更容易理解。

    【讨论】:

    • 感谢您的解释。我希望我能很快发现一个用例,这样我就可以理解它背后的全部原因。
    • @Jiji 不知道你想学什么,但我可以推荐一本 Microsoft .NET Architecture 电子书(免费)并很好地解释:dotnet.microsoft.com/learn/web/aspnet-architecture
    • 所以根据我编辑的答案,我认为调用 2 不会执行异步等待 - 在特殊的 asyn Main() 情况下 - 它会在幕后执行 WriteTextAsync().Wait()
    • @pteberf 这无关紧要,这只是未来可能会改变的实现细节
    【解决方案3】:

    当你说WriteText() 时,WriteText() 将阻塞你当前的线程直到它完成,因为它是同步的。

    当您说await WriteTextAsync() 时,您将生成一个新线程,并且您不会阻塞不依赖于WriteTextAsync() 结果的计算。
    编辑:根据Microsoft Docs 当您说await WriteTextAsync() 时,编译器会安排Main() 的其余部分在WriteTextAsync() 完成后执行。然后控件应该返回给异步方法的调用者。但是Main()的来电者是谁?因为它是turns out,所以没有async Main() - 它实际上是相同的同步Main() - 它只是语法糖,因为它没有在其中写入.Wait()。所以在这种情况下,这个调用相当于WriteTextAsync().Wait()!

    最后,当你说WriteTextAsync().Wait()时,你再次阻塞当前线程并等待WriteTextAsync()的结果。

    【讨论】:

    • 很抱歉,您的第二句话没有一处是正确的。 await 调用不会跨越新线程,因为 await 是一个等待,一个异步但仍然是一个等待,进一步的处理被锁定
    • @CamiloTerevinto 感谢您的更正-您是对的。我阅读了它并进行了编辑。
    • 但是async main 是否存在实际上与这一点无关。所有应用程序都以单线程开始,好处主要不在于控制台应用程序
    • @CamiloTerevinto 是的,但问题包括async Main()
    猜你喜欢
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多