【发布时间】: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 上。
标签: c# asp.net-mvc multithreading async-await threadpool