c# – async / await如何帮助ASP.Net应用程序?

在MVC控制器的操作方法中使用async / await可以扩展Web应用程序,因为等待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会在await之前和之后使用不同的线程.

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

解决方法:

我认为你混淆游泳池队列. ASP.NET请求有5个位置可以在IIS服务器上排队.

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

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

当您使用async / awat时,您正在使用ASP.NET:CLR线程池队列.

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

另一方面,here there is a wonderful article关于scalling web应用程序与async / await可以帮助你.

[编辑]我刚刚发现this article关于请求排队也可以帮助.

上一篇:java-ThreadPool中任务的自定义顺序


下一篇:java-当一个线程失败时,停止ExecutorService线程.并返回异常