将一个非异步方法(其中包含Web调用)包装成异步方法

6
我知道你应该只在不“占用CPU”的情况下使用async,例如文件写入、Web调用等。因此我也知道把每个方法都包装到Task.Run或类似的东西中是没有意义的。

然而,当我知道一个方法进行了Web调用,但它没有提供异步接口时,我该怎么办呢?在这种情况下,将其包装起来是否值得呢?

具体例子:

我正在我的WebApi应用程序(服务器)中使用CSOM(Client SharePoint Object Model)并想要获取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;
    }
}

这样做有意义吗?从我的理解来看,我只需要创建 / 需要一个新线程来执行查询("overhead"),但至少请求线程将会空闲 / 准备好处理另一个请求(这是很好的)。

但这样做是否值得和应该这样做呢?

如果是这样:
那么 Microsoft 为什么不提供 "async" 方法呢?或者他们只是不关心它?

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


不要使用Task.Factory.StartNew,而是使用Task.Run。至于调用SharePoint,上下文具有ExecuteQueryAsync方法,可以适应返回任务。 - Panagiotis Kanavos
1
CSOM自2010年起就有异步的ExecuteQueryAsync方法。您正在使用哪个SharePoint版本,2010、2013还是Online?每个版本都有不同的CSOM包。 - Panagiotis Kanavos
在最新版本中使用 Microsoft.SharePointOnline.CSOM,人们会认为这是最新的、最新的 CSOM……但我找不到你在那里提到的这个 async 方法。 - OschtärEi
1
你说的方法是指 docs 中提到的吗?尽管我看到引用的 DLL 文件仅适用于 Silverlight。看起来他们只在那里添加了这个方法,但文档页面不完整。我忘记了 API 和文档有多么直观。无论如何,CSOM 最终只是一组 REST 调用。如果你关心可扩展性,你可以随时使用 HttpClient 或 OData。 - Panagiotis Kanavos
1
现在你会明白为什么有些人自称为SharePoint难民了。 - Panagiotis Kanavos
显示剩余2条评论
2个回答

5
然而,当我知道一个方法进行网络调用但它不提供异步接口时该怎么办呢?
很遗憾,这种情况仍然很常见。当不同的库更新他们的API时,他们最终会追上潮流。
在这种情况下包装它值得吗?
如果你正在处理UI线程,那么是的。否则,不需要。
具体例子...在我的WebApi应用程序中(服务器)。
那么,在这种情况下,您不需要用Task.Run进行包装。正如我在有关async ASP.NET的文章中所指出的: 您可以通过等待Task.Run来启动一些后台工作,但这样做是没有意义的。实际上,这将通过干扰ASP.NET线程池启发式算法来损害可扩展性……通常情况下,不要将工作排队到ASP.NET的线程池中。
在ASP.NET中使用Task.Run进行包装:
干扰了ASP.NET线程池启发式算法两次(现在取一个线程,稍后释放它)。
增加了开销(代码必须切换线程)。
不释放线程(对于此请求使用的总线程数几乎等于只调用同步版本)。
据我所知,我只需为执行查询创建/需要一个新线程(“开销”),但至少请求线程将被释放/准备好另一个请求(这是好的)。
是的,但你所做的就是跳线程,没有任何好处。用于阻塞查询结果的线程是ASP.NET用来处理请求的线程之一,因此通过消耗另一个线程来释放一个线程并不是一个好的权衡。
Microsoft没有直接提供“async”方法,还是他们不关心它?
一些“旧”的MS API还没有添加“async”版本。他们肯定应该这样做,但开发人员时间是有限的资源。

完美的答案,正是我在寻找的 - 谢谢! - OschtärEi

0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接