我觉得我已经阅读了成千上万个有关此问题的SO问题,博客文章和MSDN文章,但我仍然没有完全“理解”它。所以请帮帮我。
在我的工作中,我们有一个ASP.NET应用程序,可以从同步方法获取数据。我无法控制这种方法,也不清楚如何实现。
我有一个异步控制器方法正在调用其他3个异步服务,我可以“一直使用”异步(是!)。但是这最后一个不是异步的,可能永远不会。更糟糕的是,它花费的时间最长。我希望能够启动所有服务的任务,并等待所有任务。根据我的拼凑而成,我可以使用以下选项,我认为我了解大多数后果,但希望在必要时进行确认和澄清。
因此,给定方法:
public async Task<Thing> GetThing()
{
var task1 = GetProp1Async(); // etc.
var syncResult = ......
myThing.Prop1 = await task1;
myThing.SyncProp = .......
return myThing;
}
我要填写这些点的选项可能是:
选项1:
只需同步调用该方法。这将阻止我当前的线程。我的其他任务都可以运行,我可以稍后再等待,但是我将在异步方法中在此处阻止。
var syncResult = GetSyncResult();
选项2:
使用Task.Run()。Result。我知道这根本不理想,因为我将生成一个新线程,同时还会阻塞我的原始线程,我认为这是最坏的情况,对吗?但是,我不会遇到死锁问题,因为我强迫 新线程,对吧?
var syncResult = Task.Run(() => GetSyncResult()).Result;
选项3:
我认为这是最好的选择?我的异步代码不会阻止线程,但是我仍在生成新的线程来进行同步工作。因此,这感觉像是净零收益。我仍然有1个线程正在处理事务,并且可能正在等待,但这仍然是我最好的选择吗?
var syncResult = await Task.Run(() => GetSyncResult());
选项4:
我仍然不了解ConfigureAwait(false),但是我可以尝试。这对我有什么帮助?似乎有些帖子说“在所有地方都这样做!”有人说“不要这样做以避免死锁”。其他人则说,如果您这样做,请“一直进行下去”。好吧,我不知道GetSyncResult()的作用,所以我也不知道它是否在后台调用了任何异步的东西(我的意思是,它可能没有,但是我不知道)。那可以再咬我吗?
var syncResult = await Task.Run(() => GetSyncResult()).ConfigureAwait(false);
那我最好的选择是什么?我有错过任何选择吗?更糟糕的是,我错过了最佳选择吗?
我知道这些问题已经被提出并被回答死了,但是,我只是没有联系上我,我确实需要一些帮助。
答案 0 :(得分:0)
选项在很大程度上取决于代码的上下文。
如果您的代码(示例中的GetThing(),实际上应该是GetThingAsync)是经典的ASP.NET操作还是UI应用程序的事件处理程序(根据您的回答,我认为是),那么您是一个重要的线程,通常数量有限,因此不要阻塞此特定线程是很重要的,因为它很特殊。选项3是最好的答案,因为它释放了重要的线程以使其返回UI / Web内容,并将工作分流到不重要的线程池线程中。但是,重要的是,在完成昂贵的工作后,请重新输入重要的上下文,以便您可以执行诸如访问HttpContext或UI元素之类的事情。
如果您的代码在某些库中,其他人将要使用,并且绝对不要阻塞线程,那么这很重要,选项4是“正确”的答案。如您所说,互联网上有数十篇博客文章详细介绍了这些文章的工作方式,因此,在这里我不再赘述,因为您已经阅读了它们。调用者可以决定通过调用ConfigureAwait(false)(不重新输入)或直接等待(它将重新输入)来尝试租用重要线程上下文。 >
选项1是最容易且最不可能出现一些意外错误的选项。除非您在ASP.NET应用程序上遇到容量问题,并且知道此调用正在执行此操作,否则建议您坚持使用简单的方法。您以后总是可以添加复杂性,但是要查找异步代码中的错误可能会非常痛苦,因此尝试以“正确”方式而不是简单方式进行操作可能最终要付出的代价超出了其实际价值,直到您知道它确实是一个问题,而不仅仅是理论上的问题。