是什么使这些异步方法调用不同?

时间:2018-12-15 18:29:21

标签: c# asynchronous async-await

我正在阅读一本有关C#书中的await和async关键字的章节。 主要是在解释这些方法调用,其中调用者使用await关键字等待被调用方法完成。

在这个简单的示例中,我认为这三个调用没有优势,但更重要的是没有区别。谁能解释这对应用程序流程有何影响?仅当调用线程是主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:如果该方法的调用线程仍在等待该方法完成,那么它是否也可能是正常调用?

3 个答案:

答案 0 :(得分:1)

据我了解,您的问题是

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

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

我将尝试从网络应用程序的角度进行解释。

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

enter image description here

图1同步等待外部资源

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

在异步调用中,线程不会被卡住,而是会被释放并返回到线程池,这将有助于为第三个调用提供服务。

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

简单地类似于aync调用的下图

enter image description here

很多事情发生在引擎盖下。我是根据Async Programming : Introduction to Async/Await on ASP.NET和我的理解写的。强烈建议在使用async-wait之前先有清楚的了解。

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

我将指的是:

//Call 1
WriteText();

//Call 2    
await WriteTextAsync();

//Call 3
WriteTextAsync().Wait();

如果您要执行的是同步等待,则第一个调用没有任何问题。在控制台应用程序中,这是很正常的。

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

使用await的呼叫2执行异步等待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");
}

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

当调用方法不能为async且必须调用async方法时,必须执行第3号呼叫。

// 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();
}

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