是否有可能知道AsyncLocal何时被彻底失效?

时间:2018-04-18 08:23:22

标签: c# .net async-await task-parallel-library

考虑我在xUnit测试类中使用的以下代码:

public sealed class AsyncLocalTests
{
    readonly ITestOutputHelper _output;

    public AsyncLocalTests(ITestOutputHelper output) => _output = output;

    [Fact]
    void Expected()
        => new AsyncLocal<object>(args =>
            {
                if (args.ThreadContextChanged && args.CurrentValue == null)
                {
                    _output.WriteLine("Invalidated!");
                }
            }) {Value = new object()};

    [Fact]
    Task ExpectedAsync() => Task.Run(action: Expected);

    [Fact]
    Task UnexpectedAsync()
        => Task.Run(async () =>
            {
                new AsyncLocal<object>(args =>
                {
                    if (args.ThreadContextChanged && args.CurrentValue == null)
                    {
                        _output.WriteLine("Invalidated!");
                    }
                }) {Value = new object()};
                await Task.Yield();
            });

}

我正在尝试使用AsyncLocal为我正在设计的应用程序存储环境值。在某些时候,创建AsyncLocal的执行范围结束,使环境值无效。我想做的是知道这种情况何时发生并且有某种事件和/或某种触发器在发生时会通知我。

在上面的三个测试中,ExpectedExpectedAsync打印出"Invalidated!"消息一次,这是我的方案的预期(所需)行为。

但是,UnexpectedAsync会将此消息打印两次。对于我的场景,这是一个问题,因为失效应该只发生一次,并且只有在创建AsyncLocal的上下文的执行已经完成执行时才会发生。

据我了解,UnexpectedAsync中的任务正在控制异步基础架构(第一次失效),然后控制权重新恢复到原始上下文(UnexpectedAsync),然后依次进入完成(第二次失效)。

我希望在这里实现的目的是确保AsyncLocal对于上述所有场景都确定无效。也就是说,我想知道AsyncLocal的执行上下文何时完成且不再有效。这可能吗?

1 个答案:

答案 0 :(得分:2)

您遇到的是documented behavior

只要有上下文切换,就会调用通知方法。 Task.Yield导致上下文切换。然后,Yield完成,并且有一个上下文切换回来检查等待状态(已完成或出现故障),然后运行异步工作的线程完成。

在您的澄清中,您使用术语&#34;已完成的事件&#34;:最好将其称为&#34;上下文切换事件&#34;。这会给你一个更好的心理模型。

回答您的问题:任务完成或出现故障后,上下文无效。等待任务将告诉您,或者您可以检查任务的IsCompleted或IsFaulted属性。