C# async/await在ASP.Net应用程序中有何帮助?

C# async/await在ASP.Net应用程序中有何帮助?,c#,asp.net-mvc,multithreading,async-await,threadpool,C#,Asp.net Mvc,Multithreading,Async Await,Threadpool,使用MVC控制器的async/await in action方法可以扩展web应用程序,因为在await时,Asp.Net线程池的请求线程被释放,以便它可以处理IIS队列中此工作进程的其他请求。这意味着,如果我们将工作进程的队列长度限制为10,并向异步操作发送50-100个请求,IIS不应返回HTTP 503错误,因为Asp.Net线程池中总会有一个空闲线程来处理传入的请求 我有一个WebApi,可以进行如下计算: public class ValuesController : ApiContr

使用MVC控制器的async/await in action方法可以扩展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);
    }
}
公共类Thread饥饿控制器:控制器
{
公共异步任务调用GetSumWithDelayAsync(int num1=5,int num2=8,int timeDelay=60)
{
int callingThreadId=Thread.CurrentThread.ManagedThreadId;
Thread饥饿模型=新的Thread饥饿模型();
字符串url=”http://localhost:8111/api/values/GetSum?x=“+num1+”&y=“+num2+”&timeDelay=“+timeDelay;
使用(HttpClient=new HttpClient())
{
//这里仍然使用相同的请求线程。。。
//下面的行将强制wait释放请求线程,并异步//等待响应。
model.ResponseFromWebService=await client.GetStringAsync(url);
//在这里,我们可以在同一个请求线程或任何其他线程上……更有可能在//另一个线程上,而不是
//请求线程。
}
int returningThreadId=Thread.CurrentThread.ManagedThreadId;
model.CallingReadId=CallingReadId;
model.ReturningThreadId=ReturningThreadId;
返回此.View(model);
}
}
WebApi和MVC托管在IIS上。MVC网站仅限于队列中的10个请求

当客户端在15或20个请求后调用MVC async方法时,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);
List taskList=new List();
对于(int x=0;x<30;x++)
{
字符串url=”http://localhost:8333/ThreadStarvation/CallGetSumWithDelayAsync?num1=“+x+”&num2=“+x+”&timeDelay=1”;
Task stringDataTask=新任务(()=>
{
使用(HttpClient-webClient=new-HttpClient())
{
字符串数据=webClient.GetStringAsync(url).Result;
WriteLine(“{0}”,数据);
}
});
任务列表。添加(stringDataTask);
}
DateTime startTime=DateTime.Now;
Parallel.ForEach(任务列表,item=>item.Start());
Console.WriteLine(“开始{0}=======================================================”,开始时间);
Task.WaitAll(taskList.ToArray());
DateTime endTime=DateTime.Now;
Console.WriteLine(“============================================================================”,endTime);
当这个运行时,在大约20个请求之后,我会收到HTTP503错误消息

如果我使用同步MVC操作,结果仍然是一样的。我知道,对于async/await,在await前后使用不同的线程


我想证明的是,使用async/await可以扩展web应用程序

我想你把泳池排队弄糊涂了。在IIS服务器上,ASP.NET请求可以在5个位置排队

  • 应用程序池队列
  • IIS工作进程
  • 线程池队列
  • 集成模式全局队列
  • 经典模式应用程序队列
  • 您设置为10的队列长度是HTTP.SYS:Application pool Queue

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

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

    另一方面,关于使用async/await缩放web应用程序,它可以帮助您


    [编辑]我刚刚发现请求队列也有帮助。

    哪个MVC抛出503?您的
    线程饥饿控制器
    控制器?您的控制器是否托管在不同的应用程序池中?如果您的API托管在与web应用程序相同的应用程序池中,则它共享一个线程池。这意味着,当用于执行MVC操作的线程被释放时,API仍在使用
    n
    线程。WebAPi和MVC网站都位于单独的应用程序池中,因此需要单独的工作进程。Yuval Itzchakov:Thread饥饿控制器托管在自己的应用程序池中,并将IIS池队列长度限制为10。是的,MVC和WebApi都托管在IIS上,并有自己的应用程序池。谢谢您。我已经阅读了“Stephen Cleary的文章,而这正是我决定编写此应用程序的时候。从您的回答来看,ASP.Net的请求线程和由await执行的后台线程是否真的取自ASP.Net线程池?@user1039644:没有由await执行的“后台线程”“.
    await
    的全部意义在于它不使用线程。@Stephen Cleary。不确定OP是否理解我。我不是这个领域的专家。你能比我更清楚地说明情况吗?@user1039644:我不知道你为什么看到503;在你的例子中有很多运动部件。我有一个简单的概念验证测试。关于您的
    await
    问题:
    await Method()
    var task=Method()相同;等待任务
    ,因此该方法实际上是在
    wait
    @user1039644之前调用的:实际上有两种不同类型的任务:在线程池上执行委托任务;他们是那种