c# - 使用 PublicationOnly 导致异步延迟死锁
问题描述
假设以下代码
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
Lazy<TimeSpan> lm = new Lazy<TimeSpan>(GetDataAsync1, System.Threading.LazyThreadSafetyMode.PublicationOnly);
return new string[] { "value1", "value2", lm.Value.ToString() };
}
private TimeSpan GetDataAsync1()
{
return GetTS().ConfigureAwait(false).GetAwaiter().GetResult();
}
// I Cant change this method, and what is inside it...
private async Task<TimeSpan> GetTS()
{
var sw = Stopwatch.StartNew();
using (var client = new HttpClient())
{
var result = await client.GetAsync("https://www.google.com/");
}
sw.Stop();
return sw.Elapsed;
}
}
关键是我正在从远程服务器获取一些数据,并希望将其缓存以备后用。由于远程服务器可能在给定点失败,我不想缓存异常,但只有成功结果......所以保持而不是等待值对我不起作用
// Cant use this, because this caches failed exception as well
Lazy<Task...> lz = ...
await lz.Value
但是上面剪断了,正如预期的那样会产生死锁,因为我无法更改 GetTS,是否可以强制 Lazy 使用我的逻辑工作?
解决方案
这个问题实际上与Lazy<T>
. 死锁是因为它阻塞了异步代码。
在这段代码中:
private TimeSpan GetDataAsync1()
{
return GetTS().ConfigureAwait(false).GetAwaiter().GetResult();
}
ConfigureAwait(false)
什么都不做。ConfigureAwait
配置awaits,而不是tasks,并且那里没有await
。
最好的选择是async
一路走。如果异常是一个问题,您可以使用AsyncLazy<T>
并传递AsyncLazyFlags.RetryOnFailure
.
如果你不能async
一路走,下一个最好的选择是一路同步。如果您不能执行其中任何一项,那么您将不得不选择sync-over-async hack;请注意,没有适用于所有情况的 hack。
推荐阅读
- c# - 如何使用 Blazor 组件访问当前路由
- c# - 如何在特定时间范围内删除 C# 中的所有 mongo 文档
- python-3.x - 在预测测试结果时,reshape 函数导致错误。(AttributeError: 'Series' object has no attribute 'reshape')
- redis - 保存redis的执行状态或关闭后恢复(不是数据库而是redis数据)
- xamarin.forms - Xamarin 表单:如何仅为 Collectionview 中的选定项目添加下划线
- c# - 将子类对象转换为基类对象
- java - 当一个对象在堆转储分析中仅被“this$0”引用时,这意味着什么?
- c# - C#如何准备项目模板/骨架
- ubuntu - 使用 setup.py 错误在 Apache2 上安装 Trac
- mvvm - SwiftUI 最简单的 MVVM