对象引用丢失在多层异步等待操作
本文关键字:异步 等待 操作 对象引用 | 更新日期: 2023-09-27 18:19:03
我遇到了一些行为,这些行为让我对垃圾收集器的异步等待模式的行为感到有点困惑。下面是对生产代码的解释。
public async Task CreateProduct(int id)
{
Product result = factory.Create(id);
await AssignPrices(result);
GC.KeepAlive(result);
Assert.That(result.Prices.Count == 1); //this is true
}
public async Task AssignPrices(Product value)
{
foreach (var engine in pricingEngines)
{
await engine.AddPrice(value);
}
}
public class DefaultPricingEngine
{
public async Task AddPrice(Product value)
{
var price = await _externalApi.GetPrice();
value.Prices.Add(price);
}
}
class Product
{
public int Id{get;set;}
public string Name {get;set;}
public List<decimal> Prices {get;set;}
}
如果我省略GC.KeepAlive
,它似乎恢复到最初创建的产品。如果我保留它,价格会按预期增加。
到底发生了什么导致了清理…GC.KeepAlive
是假阳性的吗?
这与引用或垃圾收集无关。
你调用AssignPrices
,这是一个异步方法没有等待它。这意味着你手上有一个竞态条件。
async方法将在调用线程上同步运行,直到到达第一个await,该方法返回控制,并使用一个任务表示操作的其余部分。如果你不等待它(或同步阻塞它),调用方法将与未等待的异步操作并行运行。
使用GC.KeepAlive
只能延迟调用方法,而AssignPrices
有机会完成。
您应该等待操作完成后再继续。你还应该给Async方法添加"Async"后缀,这有助于提醒你等待它。
public async Task CreateProductAsync(int id)
{
Product result = factory.Create(id);
await AssignPricesAsync(result);
Assert.That(result.Prices.Count == 1);
}