对象引用丢失在多层异步等待操作

本文关键字:异步 等待 操作 对象引用 | 更新日期: 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);
}