我正在开发一个Web API项目,使用Azure的托管缓存服务将数据库结果缓存在内存中,以提高响应时间并减轻对数据库的重复访问。当尝试将新项放入缓存时,偶尔会抛出一个特定于缓存的异常,代码为DataCacheErrorCode.RetryLater
。为了在稍后重试而不需要阻塞此方法,我将其改为async
并使用await Task.Delay
在短时间后再次尝试。之前,开发人员在其中硬编码了一个Thread.Sleep
,这实际上损害了应用程序的性能。
现在,该方法的签名看起来类似于以下内容:
public static async Task Put(string cacheKey, object obj)
更改后,我从应用程序中所有调用以前同步版本的Put
的其他位置收到大约75个编译器警告,指示:
因为此调用未被等待,当前方法的执行在调用完成之前继续。考虑将“await”运算符应用于调用结果。
在这种情况下,由于Put
没有返回任何内容,让这个操作fire-and-forget似乎对我有意义,因为我看不到阻止调用它的方法的执行的任何理由。我只是想知道允许许多这些fire-and-forget的Task
在后台运行是否存在任何危险或陷阱,因为Put
可能经常被调用。或者,无论如何都应该等待,因为99%的时间我不会得到重试错误,而Task
几乎立即完成。我只想确保我没有因拥有太多线程(或类似的东西)而遭受任何惩罚。
await Task.Run(..)
来伪造async
处理程序,这是绝大多数尝试使用Task.Run
的情况。唯一真正的Run
用例是“fire-and-forget”。而且很容易发现,99%的“fire-and-forget” ASP.NET场景都不应该使用“fire-and-forget”。更新缓存是极少数可以接受“fire-and-forget”(和Task.Run
)的用例之一。关于请求上下文,我的意思是该请求的AspNetSynchronizationContext
,当您使用async
时,默认情况下会捕获它(正如我在我的博客中所描述的)。 - Stephen ClearyConfiguareAwait(false)
是否比使用Task.Run
更可取? - Yuval Itzchakovawait Task.Yield().ConfigureAwait(false)
将会暂停当前线程,并在线程池线程上恢复方法;而Task.Run
则将其委托排队到线程池中。我认为Task.Run
有更清晰的意图(代码与上下文分离),但ConfigureAwait
可能更有效率。 - Stephen Cleary