首先,一些背景(原谅双关语)。请考虑以下两种异步方法:
public async Task Async1() {
PreWork();
await Async2();
PostWork();
}
public async Task Async2() {
await Async3();
}
感谢async
和await
关键字,这会产生一个漂亮的简单调用堆栈的错觉:
但是,如果我理解正确的话,在生成的代码中,对PostWork
的调用将作为Async2
任务的继续进行处理,因此在运行时执行流程实际上更像是这样:
(实际上它甚至更复杂,因为实际上使用async
和await
会导致编译器为每个异步方法生成状态机,但我们也许能够忽略此问题的详细信息)
现在,我的问题:有没有办法通过这些自动生成的延续来传递某种上下文,这样当我点击PostWork
时,我已经从Async1
和Async2
?
我可以使用AsyncLocal和CallContext.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
的实际链中。
编辑:我不想仅仅传递参数和返回值来解决这个问题,因为我需要一个通用的解决方案,它可以在大型代码库中运行,而无需修改每个方法来传递这个元数据。