我遇到过一些让我对垃圾收集器异步等待模式的行为感到困惑的行为。这是对生产代码的解释。
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
的误报是真正发生的。
答案 0 :(得分:3)
这与引用或垃圾收集无关。
您在不等待的情况下调用AssignPrices
这是一种异步方法。这意味着您手上有竞争条件。
异步方法将在调用线程上同步运行,直到达到第一个await,并且该方法返回带有表示操作其余部分的任务的控制。如果您没有等待它(或同步阻塞它),则调用方法将与未经同步的异步操作并行运行。
使用GC.KeepAlive
只能在AssignPrices
有机会完成时延迟调用方法。
您应该等待操作,只有在完成后才会继续。你还应该添加&#34; Async&#34;后缀为异步方法,有助于提醒您等待它。
public async Task CreateProductAsync(int id)
{
Product result = factory.Create(id);
await AssignPricesAsync(result);
Assert.That(result.Prices.Count == 1);
}