【问题标题】:The value of AsyncLocal is not correct when is set from an asynchronous method从异步方法设置时,AsyncLocal 的值不正确
【发布时间】:2021-03-12 04:35:03
【问题描述】:

为什么AsyncLocal 字段的值在从类的异步方法设置时不保留。考虑这个例子:

var scope = new TestScope();

// The default value is 0
Console.WriteLine(scope.Counter.Value);

// Setting the vlaue to 2
await scope.SetValueAsync();

Console.WriteLine(scope.Counter.Value);

class TestScope
{
    public readonly AsyncLocal<int> Counter = new AsyncLocal<int> { Value = 0 };

    public async Task SetValueAsync()
    {
        this.Counter.Value = 2;

        await Task.Yield();
    }
}

预期的输出应该是:

0

2

但实际是:

0

0

为什么退出SetValueAsync方法时异步上下文会改变?

【问题讨论】:

  • 见 J. Andrew Laughlin 的回答 here
  • 是的。我在帖子中找到了原因。但是,是否有任何解决方案可以将 AsyncLocal 字段包装在一个类中?
  • AsyncLocal&lt;T&gt; 用于传递值 down。它不打算返回up。虽然您可以破解可变包装器,但我不推荐它。在更复杂的场景中,这很容易导致其他共享问题。最好只使用AsyncLocal&lt;T&gt; 向下传递值。

标签: c# .net .net-core async-await


【解决方案1】:

这是因为 AsyncLocal 具有写时复制语义。基本上,当从内部异步范围更改时,会生成值的副本并且不会修改原始对象。

您可以通过将值包装在引用对象中来解决此问题。那是因为即使你复制了一个引用,你最终仍然在操作同一个对象:


class TestScope
{
    public readonly AsyncLocal<StrongBox<int>> Counter = new AsyncLocal<StrongBox<int>> { Value = new StrongBox<int>(0) };

    public int Value
    {
        get => Counter.Value.Value;
        set => Counter.Value.Value = value;
    }

    public async Task SetValueAsync()
    {
        this.Value = 2;

        await Task.Yield();
    }
}

static async Task Test()
{
    var scope = new TestScope();

    // The default value is 0
    Console.WriteLine(scope.Value);

    // Setting the value to 2
    await scope.SetValueAsync();

    Console.WriteLine(scope.Value);
}

StrongBox&lt;T&gt; 只是一种将值类型包装在引用类型中的便捷方式。但是任何其他引用类型都可以做到这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-29
    • 2021-12-02
    • 1970-01-01
    相关资源
    最近更新 更多