将同步代码包装成异步方法的最佳方法是啥
Posted
技术标签:
【中文标题】将同步代码包装成异步方法的最佳方法是啥【英文标题】:What is the best way for wrapping synchronous code into asynchronous method将同步代码包装成异步方法的最佳方法是什么 【发布时间】:2015-08-09 18:22:42 【问题描述】:我正在使用 async-await 方法创建一个应用程序。但是使用它们对我来说存在很大的问题。在阅读了几篇文章后,我仍然不知道将繁重的同步操作包装到异步方法的最佳方法是什么。
我有两个想法。哪个最好?
1) 当前实现。
private Task<List<UploadedTestModel>> ParseTestFiles(List<string> filesContent)
var tcs = new TaskCompletionSource<List<UploadedTestModel>>();
Task.Run(() =>
var resultList = new List<UploadedTestModel>();
foreach (var testBody in filesContent)
try
var currentCulture = Thread.CurrentThread.CurrentCulture;
var serializerSettings = new JsonSerializerSettings
Culture = currentCulture
;
var parsedData = JsonConvert.DeserializeObject<UploadedTestModel>(testBody, serializerSettings);
resultList.Add(parsedData);
catch(Exception exception)
tcs.SetException(exception);
tcs.SetResult(resultList);
);
return tcs.Task;
我正在使用 Task.Run 和 TaskCompletionSource
2) 仅使用没有 TaskCompletionSource 的 Task.Run
private Task<List<UploadedTestModel>> ParseTestFiles(List<string> filesContent)
return Task.Run(() =>
. . . .
return resultList;
);
【问题讨论】:
Task.Run 大约是您将获得的最好的 - 您不能通过仅以特殊方式调用它来使固有的同步操作真正异步。如果操作是 CPU 密集型的(或者只是包装了一些阻塞线程的东西),它需要在线程上运行。 据我所知,foreach
的主体本身可以以异步方式运行(resultList.Add
除外)。即使在那之后,我也不确定JsonConvert.DeserializeObject
是否真的从并行任务中受益。也许您应该使用信号量将并行计算限制在特定数量 - 比如 CPU 的数量?再一次,我不确定这个数字——你是在 x64 还是 x86 上,我不确定你可以从 .NET 的线程池机制中受益多少——这是底层的Task
runner(调度程序)。
& 老实说,我只是盲目地信任 Task
s 的 .NET 底层机制,我做得很好。但是对于更繁重的事情,您最好使用 Akka.NET 之类的东西。
@Egorikas 请记住 async
和 await
在 UI 应用程序中的行为有点不同 - 它们保留上下文。所以你必须使用明确的Task
启动;然后await
Task
。
@KavehShahbazian 我明白了。谢谢你的解释
【参考方案1】:
我都不会使用。你会对调用你的方法调用的人撒谎。当你公开一个异步操作时,调用者希望它自然是异步的,这意味着它背后没有线程在工作。
你所有的方法都是天生同步的,你应该这样公开它们。让调用者决定是要同步调用它们还是使用线程并在那里排队,不要为它们决定。
Stephan Toub 写了一篇名为Should I expose asynchronous wrappers for synchronous methods? 的精彩文章,其中谈到了不做你想做的事的所有原因。我建议阅读它。
【讨论】:
Lucian Wischik 有一个标题为 Async Library Methods Shouldn't Lie 的视频,它说了同样的话。 @Yuval 我读过这篇文章。之后,我开始思考我的代码。那么,你能解释一下吗?您是否提供使此方法同步,然后使用任务调用它(它阻塞了我的 UI 线程,所以我需要用它做点什么)。 在 wpf、windows 窗体、uwp 等应用程序上使用同步操作是否有意义?您阻塞了 UI 线程,它会冻结应用程序。在这种情况下,包装为 asnyc 不是更好吗? @batmaci 确实,阻塞 UI 线程是没有意义的。但是我仍然不会将方法呈现为异步的,我只是从事件处理程序中调用Task.Run
。以上是关于将同步代码包装成异步方法的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章