在具有许多异步调用的 WebAPI 中使用异步
the use of async in WebAPI with many async calls
对于初学者的问题,我很抱歉,我在这里和网上阅读了很多post,但有些基础知识我无法理解。
据我了解,在 WebAPI 中使用异步操作主要是出于可伸缩性的原因,因此任何传入请求都将被转移到工作程序而不是线程,这样就可以处理更多请求。
我的项目由几个巨大的动作组成,这些动作由 EF6 多次从数据库中 read/insert/update 完成。动作看起来像这样:
public async Task<HttpResponseMessage> Get(int id)
{
PolicyModel response = await policyRepository.GetPolicyAsync(id);
return Request.CreateResponse<PolicyModel>(HttpStatusCode.OK,response);
}
和 GetPolicyAsync(int id) 看起来像这样:
public async Task<PolicyModel> GetPolicyAsync(int id)
{
PolicyModel response = new PolicyModel();
User currentUser = await Repositories.User.GetUserDetailsAsync(id);
if(currentUser.IsNew)
{
IEnumerable<Delivery> deliveries = await Repositories.Delivery.GetAvailableDeliveries();
if(deliveries == null || deliveries.Count() == 0
{
throw new Exception("no deliveries available");
}
response.Deliveries = deliveries;
Ienumerable<Line> lines = await Repositores.Delivery.GetActiveLinesAsync();
lines.AsParallel().ForAll(line => {
await Repositories.Delivery.AssignLineAsync(line,currentUser);
}
...
return response;
}
我没有写完整的代码,但它有很多,它也分为几个方法,但这就是它的精神
现在我的问题是:在一种方法中使用这么多等待者是一种好习惯吗?我看到它更难调试,是否很难维护线程上下文以及为了工作分配,我不应该只使用 Task.Factory.StartNew()
或者调用一个简单的 Task.Delay()
以便请求会立即转移给工人吗?
我知道这不是一个好的做法(一直异步)所以可能只有 GetpolicyAsync(int id)
方法
的 end/begining 处的一个异步方法
编辑:
据我了解 .net 中异步方法的机制,对于每个异步方法,编译器都在寻找一个空闲线程并让它处理该方法,该线程正在寻找一个空闲工作者并分配该方法给它,然后向编译器报告它是免费的。所以如果我们有 10 个线程并且每个线程有 10 个 worker,那么程序可以处理 100 个并发异步方法。
所以回到网络开发,IIS 为每个应用程序池分配 x 线程,例如 10。这意味着异步 WebAPI 方法可以处理 100 个请求,但是如果内部有另一个异步方法,则可以处理的请求数量是 50 等等,对吗?
据我所知,我必须调用异步方法才能使 WebAPI 方法真正异步,现在,由于使用 Task.Factory.StartNew()
是一种不好的做法,我必须至少使用 Task.Delay()
我真正想要获得的是异步 WebAPI 方法的可扩展性和同步方法的上下文感知
在我目前看到的所有示例中,它们只显示了非常简单的代码,但在现实生活中,方法要复杂得多
谢谢
在一个方法中使用多个 await
并没有错。如果这个方法对你来说太复杂了,你可以把它分成几个方法:
public async Task<PolicyModel> GetPolicyAsync(int id)
{
PolicyModel response = new PolicyModel();
User currentUser = await Repositories.User.GetUserDetailsAsync(id);
if(currentUser.IsNew)
{
await HandleDeliveriesAsync(await Repositories.Delivery.GetAvailableDeliveries());
}
...
return response;
}
public async Task HandleDeliveriesAsync(IEnumerable<Delivery> deliveries)
{
if(deliveries == null || deliveries.Count() == 0
{
throw new Exception("no deliveries available");
}
response.Deliveries = deliveries;
Ienumerable<Line> lines = await Repositores.Delivery.GetActiveLinesAsync();
lines.AsParallel().ForAll(line => {
await Repositories.Delivery.AssignLineAsync(line,currentUser);
}
不要使用 Task.Factory.StartNew
或 Task.Delay
,因为它只是将相同的工作卸载到不同的线程。在大多数情况下,它不会增加任何价值,实际上会损害 ASP.Net。
经过大量搜索,我发现了这篇文章:
https://msdn.microsoft.com/en-us/magazine/dn802603.aspx
它解释了@i3arnon 在评论中所说的内容。根本没有工人,线程正在做所有的工作。
简而言之,处理 Web 请求的线程到达一个设计为在驱动程序堆栈上异步完成的操作,它创建一个请求并将其传递给驱动程序。
驱动程序将其标记为挂起并向线程报告"done",该线程返回线程池以获取另一个分配。
实际工作不是由线程完成的,而是由驱动程序完成的,该驱动程序从所有线程中借用 cpu 时间并让它们自由地处理它们的业务。
当操作完成时,驱动程序会通知它并且有一个可用的线程来继续...
因此,从中我了解到我应该研究每一个异步方法并检查它是否确实执行了异步使用驱动程序的操作。
思考要点:您网站的可扩展性取决于真正异步完成的操作数量,如果您没有任何操作,那么您的网站将无法扩展。
版主,我是新手,如有不妥之处还望指正
谢谢!
对于初学者的问题,我很抱歉,我在这里和网上阅读了很多post,但有些基础知识我无法理解。
据我了解,在 WebAPI 中使用异步操作主要是出于可伸缩性的原因,因此任何传入请求都将被转移到工作程序而不是线程,这样就可以处理更多请求。
我的项目由几个巨大的动作组成,这些动作由 EF6 多次从数据库中 read/insert/update 完成。动作看起来像这样:
public async Task<HttpResponseMessage> Get(int id)
{
PolicyModel response = await policyRepository.GetPolicyAsync(id);
return Request.CreateResponse<PolicyModel>(HttpStatusCode.OK,response);
}
和 GetPolicyAsync(int id) 看起来像这样:
public async Task<PolicyModel> GetPolicyAsync(int id)
{
PolicyModel response = new PolicyModel();
User currentUser = await Repositories.User.GetUserDetailsAsync(id);
if(currentUser.IsNew)
{
IEnumerable<Delivery> deliveries = await Repositories.Delivery.GetAvailableDeliveries();
if(deliveries == null || deliveries.Count() == 0
{
throw new Exception("no deliveries available");
}
response.Deliveries = deliveries;
Ienumerable<Line> lines = await Repositores.Delivery.GetActiveLinesAsync();
lines.AsParallel().ForAll(line => {
await Repositories.Delivery.AssignLineAsync(line,currentUser);
}
...
return response;
}
我没有写完整的代码,但它有很多,它也分为几个方法,但这就是它的精神
现在我的问题是:在一种方法中使用这么多等待者是一种好习惯吗?我看到它更难调试,是否很难维护线程上下文以及为了工作分配,我不应该只使用 Task.Factory.StartNew()
或者调用一个简单的 Task.Delay()
以便请求会立即转移给工人吗?
我知道这不是一个好的做法(一直异步)所以可能只有 GetpolicyAsync(int id)
方法
编辑:
据我了解 .net 中异步方法的机制,对于每个异步方法,编译器都在寻找一个空闲线程并让它处理该方法,该线程正在寻找一个空闲工作者并分配该方法给它,然后向编译器报告它是免费的。所以如果我们有 10 个线程并且每个线程有 10 个 worker,那么程序可以处理 100 个并发异步方法。
所以回到网络开发,IIS 为每个应用程序池分配 x 线程,例如 10。这意味着异步 WebAPI 方法可以处理 100 个请求,但是如果内部有另一个异步方法,则可以处理的请求数量是 50 等等,对吗?
据我所知,我必须调用异步方法才能使 WebAPI 方法真正异步,现在,由于使用 Task.Factory.StartNew()
是一种不好的做法,我必须至少使用 Task.Delay()
我真正想要获得的是异步 WebAPI 方法的可扩展性和同步方法的上下文感知
在我目前看到的所有示例中,它们只显示了非常简单的代码,但在现实生活中,方法要复杂得多
谢谢
在一个方法中使用多个 await
并没有错。如果这个方法对你来说太复杂了,你可以把它分成几个方法:
public async Task<PolicyModel> GetPolicyAsync(int id)
{
PolicyModel response = new PolicyModel();
User currentUser = await Repositories.User.GetUserDetailsAsync(id);
if(currentUser.IsNew)
{
await HandleDeliveriesAsync(await Repositories.Delivery.GetAvailableDeliveries());
}
...
return response;
}
public async Task HandleDeliveriesAsync(IEnumerable<Delivery> deliveries)
{
if(deliveries == null || deliveries.Count() == 0
{
throw new Exception("no deliveries available");
}
response.Deliveries = deliveries;
Ienumerable<Line> lines = await Repositores.Delivery.GetActiveLinesAsync();
lines.AsParallel().ForAll(line => {
await Repositories.Delivery.AssignLineAsync(line,currentUser);
}
不要使用 Task.Factory.StartNew
或 Task.Delay
,因为它只是将相同的工作卸载到不同的线程。在大多数情况下,它不会增加任何价值,实际上会损害 ASP.Net。
经过大量搜索,我发现了这篇文章: https://msdn.microsoft.com/en-us/magazine/dn802603.aspx
它解释了@i3arnon 在评论中所说的内容。根本没有工人,线程正在做所有的工作。
简而言之,处理 Web 请求的线程到达一个设计为在驱动程序堆栈上异步完成的操作,它创建一个请求并将其传递给驱动程序。
驱动程序将其标记为挂起并向线程报告"done",该线程返回线程池以获取另一个分配。
实际工作不是由线程完成的,而是由驱动程序完成的,该驱动程序从所有线程中借用 cpu 时间并让它们自由地处理它们的业务。
当操作完成时,驱动程序会通知它并且有一个可用的线程来继续...
因此,从中我了解到我应该研究每一个异步方法并检查它是否确实执行了异步使用驱动程序的操作。
思考要点:您网站的可扩展性取决于真正异步完成的操作数量,如果您没有任何操作,那么您的网站将无法扩展。
版主,我是新手,如有不妥之处还望指正 谢谢!