流生成上下文/状态到生成的延续

时间:2015-06-04 15:17:47

标签: c# .net async-await

首先,一些背景(原谅双关语)。请考虑以下两种异步方法:

public async Task Async1() {
    PreWork();
    await Async2();
    PostWork();
}

public async Task Async2() {
    await Async3();
}

感谢asyncawait关键字,这会产生一个漂亮的简单调用堆栈的错觉:

  • Async1
    • PreWork
    • Async2
      • Async3
    • PostWork

但是,如果我理解正确的话,在生成的代码中,对PostWork的调用将作为Async2任务的继续进行处理,因此在运行时执行流程实际上更像是这样:

  • Async1
    • PreWork
    • Async2
      • Async3
        • PostWork

(实际上它甚至更复杂,因为实际上使用asyncawait会导致编译器为每个异步方法生成状态机,但我们也许能够忽略此问题的详细信息)

现在,我的问题:有没有办法通过这些自动生成的延续来传递某种上下文,这样当我点击PostWork时,我已经从Async1Async2

我可以使用AsyncLocalCallContext.LogicalSetData之类的工具获得类似的内容,但它们并不是我需要的东西,因为上下文已“回滚” “当你按照异步链的方式工作时。例如,在以下代码中调用Async1将打印“1,3”,而不是“1,2,3”:

private static readonly AsyncLocal<ImmutableQueue<String>> _asyncLocal = new AsyncLocal<ImmutableQueue<String>>();

public async Task Async1() {
    _asyncLocal.Value = ImmutableQueue<String>.Empty.Enqueue("1");
    await Async2();
    _asyncLocal.Value = _asyncLocal.Value.Enqueue("3");
    Console.WriteLine(String.Join(",", _asyncLocal.Value));
}

public async Task Async2() {    
    _asyncLocal.Value = _asyncLocal.Value.Enqueue("2");
    await Async3();
}

我理解为什么这会打印“1,3”(执行上下文向下流到Async2但不会回到Async1)但这不是我想要的。我真的想通过实际执行链来积累状态,这样我就可以在最后打印“1,2,3”,因为这是执行方法的实际方式直至致电Console.WriteLine

请注意,我不想盲目地在所有异步工作中累积所有状态,我只想积累与因果关联的状态。在这种情况下,我想打印“1,2,3”,因为“2”来自一个依赖(等待)的任务。相反,如果调用Async2只是一个即发即弃的任务,那么我不希望看到“2”,因为它的执行不会出现在导致Console.WriteLine的实际链中。

编辑:我不想仅仅传递参数和返回值来解决这个问题,因为我需要一个通用的解决方案,它可以在大型代码库中运行,而无需修改每个方法来传递这个元数据。

0 个答案:

没有答案