async / await 如何在 ASP.Net 应用程序中提供帮助?

Posted

技术标签:

【中文标题】async / await 如何在 ASP.Net 应用程序中提供帮助?【英文标题】:How async / await can help in ASP.Net application? 【发布时间】:2015-03-07 11:14:42 【问题描述】:

在 MVC 控制器的 action 方法中使用 async / await 可以扩展 Web 应用程序,因为在 await 时,Asp.Net 线程池的请求线程被释放,以便它可以为该工作进程处理 IIS 队列中的其他请求。这意味着如果我们将工作进程的队列长度限制为 10,并向异步操作发送 50 - 100 个请求,则 IIS 不应返回 HTTP 503 错误,因为总会有一个来自 Asp.Net 线程池的空闲线程来服务器传入的请求。

我有一个 WebApi 进行如下计算:

public class ValuesController : ApiController

    public int GetSum(int x, int y, int timeDelay = 1)
    
        Thread.Sleep(timeDelay*1000);
        int result = x + y;
        return result;
    

此操作方法仅延迟给定的秒数,然后将总和结果返回给调用代码。非常基本的 web api,只是为了模仿长时间运行的代码。

接下来是等待结果的 MVC 异步操作:

public class ThreadStarvationController : Controller

    public async Task<ActionResult> CallGetSumWithDelayAsync(int num1 = 5, int num2 = 8, int timeDelay = 60)
    
        int callingThreadId = Thread.CurrentThread.ManagedThreadId;
        ThreadStarvationModel model = new ThreadStarvationModel();

        string url = "http://localhost:8111/api/values/GetSum?x=" + num1 + "&y=" + num2 + "&timeDelay=" + timeDelay;

        using (HttpClient client = new HttpClient())
        

            // here still using the same request thread...
            // following line will force await to free up the request thread and wait asynchronouly               //for the response.
            model.ResponseFromWebService = await client.GetStringAsync(url);

            // here we can be on the same request thread or anyother thread... more likely on //another other thread than 
            // request thread.
        

        int returningThreadId = Thread.CurrentThread.ManagedThreadId;

        model.CallingThreadId = callingThreadId;
        model.ReturningThreadId = returningThreadId;

        return this.View(model);
    

WebApi 和 MVC 托管在 IIS 上。 MVC 网站仅限于队列中的 10 个请求。

当客户端在 15 或 20 个请求后调用 MVC 异步方法时,IIS 发送 HTTP 503 错误,这意味着 IIS 队列已满请求。

这是调用 MVC 异步方法的控制台应用程序代码。它调度 30 个任务并并行执行。

          List<Task> taskList = new List<Task>();
        for (int x = 0; x < 30; x++)
        
            string url = "http://localhost:8333/ThreadStarvation/CallGetSumWithDelayAsync?num1=" + x + "&num2=" + x + "&timeDelay=1";

            Task stringDataTask = new Task(() =>
            
                using (HttpClient webClient = new HttpClient())
                
                    string data = webClient.GetStringAsync(url).Result;
                    Console.WriteLine("0", data);
                
            );


            taskList.Add(stringDataTask);
        

        DateTime startTime = DateTime.Now;

        Parallel.ForEach(taskList, item => item.Start());

        Console.WriteLine("================== START 0 ===========================", startTime);

        Task.WaitAll(taskList.ToArray());
        DateTime endTime = DateTime.Now;
        Console.WriteLine("================= THE END 0 ============================", endTime);

当它运行时,大约 20 个请求后,我收到 HTTP 503 错误消息。

如果我使用同步 MVC 动作,结果还是一样的。我知道在 async / await 之前和之后使用不同的线程。

我想证明的是,使用 async / await 将扩展 Web 应用程序。

【问题讨论】:

哪个 MVC 抛出了 503?你的ThreadStarvationController 控制器?您的控制器是否托管在不同的应用程序池中? 如果您的 API 托管在与您的 Web 应用程序相同的应用程序池下,则它共享一个线程池。这意味着 - 当用于执行 MVC 操作的线程被释放时 - 您的 API 仍在使用 n 线程。 WebAPI 和 MVC 网站都在不同的应用程序池中,因此是独立的工作进程。 Yuval Itzchakov:ThreadStarvationController 控制器托管在其自己的应用程序池中,并且将 IIS 池队列长度限制为 10。是的 MVC 和 WebApi 都托管在具有自己的应用程序池的 IIS 上。 【参考方案1】:

我认为您混淆了池队列。 ASP.NET 请求可以在 IIS 服务器上排队的 5 个位置。

    应用程序池队列 IIS 工作进程 CLR 线程池队列 集成模式全局队列 经典模式应用队列

您设置为 10 的队列长度是 HTTP.SYS: 应用程序池队列。

当您使用 async/awat 时,您使用的是 ASP.NET:CLR 线程池队列。

这就是为什么即使使用 async/await 也会出现 503 错误的原因。

另一方面,here there is a wonderful article 关于使用 async/await 调用 Web 应用程序可以帮助您。

[编辑] 我刚刚发现 this article 关于请求排队也有帮助。

【讨论】:

谢谢 jlvaquero。我已经阅读了“Stephen Cleary 的文章,那是我决定编写这个应用程序的时候。从你的回答来看,ASP.Net 的请求线程和 await 执行的后台线程是否取自 Asp.Net 线程池?跨度> @user1039644:没有“等待执行的后台线程”。 await 的全部意义在于它使用线程。 @Stephen Cleary。不确定OP是否理解我。我不是该领域的专家。你能比我更清楚地说明情况吗? @user1039644:我不确定你为什么会看到 503;您的示例中有很多活动部件。我有一个简单的概念验证测试here。关于您的await 问题:await Method()var task = Method(); await task; 相同,因此该方法实际上在 await 之前调用。 @user1039644:实际上有两种不同的任务:委托任务在线程池上执行;他们是大多数人想到的那种任务。但也有 Promise Tasks,它不会在任何地方“运行”;从async 方法返回的任务是 Promise 任务。我有几篇博文here 和here。另外,我的博文There Is No Thread 可能会更好地解释它。

以上是关于async / await 如何在 ASP.Net 应用程序中提供帮助?的主要内容,如果未能解决你的问题,请参考以下文章

通过 ASP.NET Web API 有效地使用 async/await

深入理解 ASP.NET MVC 上的 async/await

为啥总是在 asp.net mvc 中同步异步操作(async await)

asp.net webform中使用async,await实现异步操作

asp.net webform中使用async,await实现异步操作

使用 async/await 并从 ASP.NET Web API 方法返回 Task<HttpResponseMessage>