将非异步方法(进行Web调用)包装为异步

时间:2017-03-02 12:54:02

标签: c# asp.net-web-api sharepoint async-await csom

我知道你应该只对非“CPU密集型”的东西使用异步,例如文件写入,Web调用等因此我也知道将每个方法包装到Task.Run或类似的东西中是没有意义的。

但是,当我知道某个方法进行网络通话时,我应该怎么做,但它不提供异步接口。在这种情况下值得包装吗?

具体例子:

我在WebApi应用程序(服务器)中使用CSOM(客户端SharePoint对象模型),并希望获得SharePoint列表。

通常这样做:

[HttpGet]
[Route("foo/{webUrl}")]
public int GetNumberOfLists(string webUrl)
{
    using (ClientContext context = new ClientContext(webUrl))
    {
        Web web = context.Web; 
        context.Load(web.Lists); 
        context.ExecuteQuery(); 

        return web.Lists.Count;
    }
}

我想把它改成这样的东西:

[HttpGet]
[Route("foo/{webUrl}")]
public async Task<int> GetNumberOfLists(string webUrl)
{
    using (ClientContext context = new ClientContext(webUrl))
    {
        Web web = context.Web; 
        context.Load(web.Lists); 
        await Task.Run(() => clientContext.ExecuteQuery());

        return web.Lists.Count;
    }
}

它是否有意义并且有帮助吗?据我了解,我只是创建/需要一个新的线程来执行查询(“开销”),但至少请求线程将是空闲/准备好的另一个请求(这将是好的)。

但值得吗?应该这样做吗?

如果是这样的话: 微软不提供开箱即用的“异步”方法或他们只是不关心它并不奇怪吗?

编辑: 更新为使用评论中建议的Task.Run

2 个答案:

答案 0 :(得分:5)

  

但是,当我知道某个方法进行网络通话时,我该怎么办,但它不提供异步接口。

不幸的是仍然有些常见。随着不同的库更新其API,它们最终将迎头赶上。

  

在这种情况下值得包装吗?

是的,如果您正在处理UI线程。否则,没有。

  

具体示例...在我的WebApi应用程序(服务器)

然后,不,你不想包裹Task.Run。如我article on async ASP.NET中所述:

  

你可以通过等待Task.Run开始一些后台工作,但这样做没有意义。实际上,这实际上会通过干扰ASP.NET线程池启发式来损害您的可伸缩性......作为一般规则,不要将工作排队到ASP.NET上的线程池。

在ASP.NET上使用Task.Run包装:

  • 干扰ASP.NET线程池启发式两次(现在通过线程然后稍后释放它)。
  • 增加开销(代码必须切换线程)。
  • 不释放线程(此请求使用的线程总数几乎等于只调用同步版本。)
  

据我了解,我只是创建/需要一个新线程来执行查询(“开销”),但至少请求线程可以自由/准备好进行另一个请求(这样会很好)。

是的,但你所做的只是跳线,没有任何好处。用于阻塞查询结果的线程是ASP.NET必须用来处理请求的一个线程,因此通过使用另一个线程来释放一个线程并不是一个很好的权衡。

  

微软不提供开箱即用的“异步”方法或他们只是不关心它并不奇怪吗?

一些“较旧的”MS API尚未添加async版本。他们肯定,但开发人员时间是有限的资源。

答案 1 :(得分:0)

这是我个人对您的问题的看法,对我而言,上述方式不是必需的。当我们在IIS中托管您的API时,服务器会从服务器中的线程池中分配一个线程。 IIS还具有maxConcurrentRequestsPerCPU maxConcurrentThreadsPerCPU的设置。您可以设置这些值来提供请求,而不是自己处理请求。