在使用异步Web服务时,应该“等待”什么,不应该做什么?

时间:2015-07-23 13:28:02

标签: c# multithreading web-services asynchronous async-await

我正在开发一个MVC Web应用程序,它允许我通过Web服务异步管理我的数据。

据我所知,这允许访问运行此网站的服务器的应用程序池的CPU线程在发出请求后返回到应用程序池,以便它们可以用于服务其他请求而不会拖延整个线程。

假设我的理解是正确的(尽管措辞可能很糟糕),我开始考虑什么时候应该await。考虑以下功能:

public async Task<ActionResult> Index()
{
    List<User> users = new List<User>();
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri("http://localhost:41979");
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
                       new MediaTypeWithQualityHeaderValue("application/json"));

        HttpResponseMessage response = await client.GetAsync("api/user/");

        if (response.IsSuccessStatusCode)
        {
            users = await response.Content.ReadAsAsync<List<User>>();
        }
    }

    return View(users);
}

我的所有函数看起来都很相似,只是它们对Web服务返回的数据做了不同的事情,我想知道,我是否还等待返回?

类似的东西:

return await View(users);

await return View(users);

我的意思是网站到目前为止运行得很好,除了我对Web服务应该发送回客户端网站的内容有点困惑,但因为我是涉及Web服务的开发新手,我仍然想知道我是否正常做事,这已经在我身上吃了一段时间了。

4 个答案:

答案 0 :(得分:4)

您只能等待通过TaskTask<T>或自定义awaiter公开异步操作的命名或匿名方法和lambda表达式。由于View本身没有异步,因此您无法等待它。

使用await的重点是什么?通常,您有一些IO绑定操作,这些操作本质上是异步的。通过使用异步API,您可以通过返回线程池来允许线程无阻塞,利用它来提供不同的请求。 async不会改变HTTP的性质。它仍然是&#34;请求 - 响应&#34;。当async方法产生控制权时,不会返回对客户端的响应。它只会在动作完成后返回。

View(object obj)会返回ViewResult,而ViewResult会将您的对象转换为所需的输出。 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:param name="isbns-to-remove" select="'BBB12343'"/> <xsl:template match="@*|node()" name="identity"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="Book"> <xsl:if test="not(contains(concat(',', $isbns-to-remove, ','), concat(',', identification/isbnNumber, ',')))"> <xsl:call-template name="identity"/> </xsl:if> </xsl:template> </xsl:stylesheet> 不是等待的,因为它没有暴露任何&#34;承诺&#34;通过一个等待的对象。因此,你不能异步地等待它。

答案 1 :(得分:1)

  

我开始思考何时应该等待事情

总是等待来自异步调用的结果。

如果你没有等待,你开火&amp;忘了,在成功和错误的情况下,你都不会收到你的回复。

答案 2 :(得分:0)

你&#34;等待&#34;当您需要将异步任务解包为值时。另外,如果需要,您可以返回任务并在以后运行。另请注意.Wait()与await不同。 Wait()将阻塞,直到任务完成(我知道边注)。还要检查代码,方法签名中存在语法错误:

public async <Task>ActionResult> Index()

应该是

public async Task<ActionResult> Index()

答案 3 :(得分:0)

我认为这是一个非常好的问题,也很难回答,特别是在网站的情况下。

  

我对网络服务应该发送回客户网站的确切方面感到有些困惑&#39;

最重要的是要了解如果使用async / await,那么你的动作方法代码仍然是序列化的,我的意思是下一行只有在异步操作完成后才会执行。但是会有(至少)三个线程:

  • 到action方法所在的原始Web服务器工作线程 调用。最初MVC基础设施得到了这个线程 池,并专门为此线程提供当前请求。
  • 可选的线程由异步操作启动什么可以 随叫随叫。
  • 一个延续线程(也是一个来自的工作者) pool)在等待之后你的行动方法继续。注意 这个线程将具有相同的上下文(HttpContext,用户文化 等等什么是原始工作者,所以它对你来说是透明的,但它将是从池中新分配的另一个线程。

对于第一眼看来没有任何意义:如果动作方法中的操作仍然是序列化的话,为什么所有那些线程都会聚焦?我需要同时...为了理解这一点,我们必须看一下桌面应用程序说一个WPF应用程序。

简而言之:有一个消息循环和一个专用线程,它从队列中读取UI(和其他虚拟)事件,并在该线程的上下文中执行事件处理程序,如按钮单击事件。如果您阻止此线程2秒,UI将无法响应。这就是为什么我们希望在另一个线程中以异步方式执行许多操作,并让专用(UI)线程快速返回并处理来自队列的其他消息。然而,这可能非常不方便,因为我们有时想在异步操作结束后继续某些事情并且我们得到了结果。 C#功能async / await使这非常方便。这种情况下,延续线程始终是专用的UI线程,但在其无限循环的(非常)后一轮中。包装起来:

在事件处理程序中,您可以使用async / await仍然在序列化中执行操作,继续将始终在原始专用UI线程中完成,但在等待期间,此线程可以循环并处理其他消息。

现在回到MVC动作方法:在这种情况下,你的动作方法就是事件处理程序的类比。虽然仍然很难理解线程复杂的使用:这种阻止action方法的情况不会阻塞任何东西(因为它阻止了WPF中的专用UI线程)。以下是事实:

  • 其他客户端(浏览器)请求将由其他线程提供。 (所以我们是 通过阻止此线程(*)继续阅读
  • 来阻止任何事情
  • 即使我们使用async / await,也不会更快地提供此请求,因为 操作是序列化的,我们必须等待(等待)结果 异步操作。 (不要将async / await与并行处理混淆)

因此,使用async / await和透明线程技巧并不像桌面应用程序那样明显。

仍然:游泳池不是无穷无尽的资源。将ASAP释放回池中(通过等待)工作线程最初专用于为请求提供服务,并在完成后继续在另一个线程中执行异步操作可以更好地利用线程池和可能在极端压力下带来更好的Web服务器性能。