从异步方法同步调用CPU绑定的方法引起的困惑

9
我正在学习使用.NET 4.5的async/await构造。我正在开发一个RESTful Web API解决方案。我正在思考如何处理CPU-bound操作 - 是1)从当前线程同步调用,还是2)使用Task.Run()
让我们参考这个页面上的示例:
async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the 
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoCPUBoundWork();

    // The await operator suspends AccessTheWebAsync. 
    //  - AccessTheWebAsync can't continue until getStringTask is complete. 
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync. 
    //  - Control resumes here when getStringTask is complete.  
    //  - The await operator then retrieves the string result from getStringTask. 
    string urlContents = await getStringTask;

    // The return statement specifies an integer result. 
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value. 
    return urlContents.Length;
}

假设DoCPUBoundWork()完全绑定于CPU,并且没有任何IO操作,那么从当前线程中调用它是否是最佳实践?

还是更好的方法是采用以下方式?

await Task.Run(() => DoCPUBoundWork()).ConfigureAwait(false);

我阅读了Cleary先生的几篇文章,得到了一些混合的建议。在这篇文章中,他建议同步调用CPU密集型任务,以避免使用async/await/Task.Run()带来的不必要开销。然而,在这篇文章中,他建议对于CPU密集型操作使用Task.Run(),但没有提及任何特殊情况。我相信我应该错过了一些明显的东西。希望能够得到一些澄清。

你的 DoIndependentWork 通常运行多长时间? - Riad Baghbanli
假设在高负载下,它需要足够长的时间才会引起问题... - Zoomzoom
1个回答

7
这是最佳实践吗?在当前线程中调用它是否正确?如果您的当前线程空闲而异步操作正在进行,为什么要使用不同的线程?那个线程比您已经使用的线程更好在哪些方面?
关于 DoIndependentWork 到底在做什么,这会引发一些问题。如果它在 HTTP 请求完成之前必须完成,则应该同步调用它。如果在 HTTP 请求完成之前完成工作并不重要,则应寻找完全不同的解决方案。在 ASP.NET 中使用 Task.Run 是危险的。
请记住,ASP.NET 中的连续性在任意线程池线程上运行。

那么...什么时候使用Task.Run()将其卸载到另一个线程中才有意义呢?(我很困惑为什么Stephen Cleary在他的博客中建议对于CPU密集型任务使用Task.Run()) - Zoomzoom
2
@Andreas 你说得对,如果这是WPF,我会给出不同的建议。但这是ASP.NET,与之不同。 - Yuval Itzchakov
关于您回答中的附加评论:在我的情况下,DoIndependentWork() 可能是一些数据的反序列化(它并不完全是“独立的”)。它必须在请求完成之前完成。现在我意识到我不应该使用这个例子,因为方法的名称并没有帮助我的问题。我将编辑我的问题。 - Zoomzoom
2
@Zoomzoom 在 ASP.NET 中,你甚至不应该使用 Task.Run。它不会进行工作注册,而且在执行期间 IIS 可能会回收你的应用程序。在基于 UI 的应用程序中使用 Task.Run 更有意义,我猜这就是 Stephan 所指的。 - Yuval Itzchakov
1
我应该在他的博客文章下面读评论。有人已经问了同样的问题,而且Stephen也表示赞同你的看法。 - Zoomzoom
显示剩余2条评论

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