我正在尝试找到一种方法来跟踪异步任务的执行流程,以便于理解有关任务的方式,启动它的原始流程是什么。我主要需要它来记录、调试和保存特定执行流的某种堆栈跟踪。
例如:如果我的服务器有来自多个 IP 的许多客户端,并且服务器需要为每个客户端执行一个涉及许多异步操作的工作流,因此每个执行流涉及许多不同的任务;记录这样的流程很困难,尤其是在使用 async/await
机制时。
我还没有想出一种方法来包装任务,以便对于执行的每个任务我都知道记录时的初始流程描述。例如,如果我为一个带有描述的操作开始一个新的任务流程——“照顾 10.0.3.4 客户端”,我希望能够为从这个流程中产生的每个日志项添加这个描述,在从它创建的任何任务中.
当只使用线程时,这很容易,因为你有线程静态变量。任务是不可能的...
我什至尝试创建自己的任务调度程序,它将包装任何使用它的任务(即使在使用 async/await
方法时)但陷入死胡同,因为任务调度程序基础有时会使用新线程(即使没有任何使用新线程的隐式请求)- TryExecuteTaskInline
方法有时可以在新线程中运行。
关于如何实现该目标的任何想法或建议?
最佳答案
您可以使用 Trace.CorrelationManager.ActivityId
存储逻辑操作 ID,或者更好地存储 ImmutableStack
逻辑操作 ID。它存储在 CallContext
中并通过 async
方法调用复制:
public static class LogicalFlow
{
private static readonly string _name = typeof (LogicalFlow).Name;
private static ImmutableStack<Guid> LogicalStack
{
get
{
return CallContext.LogicalGetData(_name) as ImmutableStack<Guid> ?? ImmutableStack.Create<Guid>();
}
set
{
CallContext.LogicalSetData(_name, value);
}
}
public static Guid CurrentId
{
get
{
var logicalStack = LogicalStack;
return logicalStack.IsEmpty ? Guid.Empty : logicalStack.Peek();
}
}
}
您可以将它用作 IDisposable
,这样您就可以利用 using
范围来确保每个 Push 都有一个
:Pop
private static readonly Popper _popper = new Popper();
public static IDisposable StartScope()
{
LogicalStack = LogicalStack.Push(Guid.NewGuid());
return _popper;
}
private sealed class Popper : IDisposable
{
public void Dispose()
{
LogicalStack = LogicalStack.Pop();
}
}
用法:
using (LogicalFlow.StartScope())
{
Console.WriteLine(LogicalFlow.CurrentId);
await DoSomethingAsync();
Console.WriteLine(LogicalFlow.CurrentId);
}
这个答案以前依赖于 Trace.CorrelationManager.LogicalOperationStack
但 Is LogicalOperationStack
incompatible with async in .Net 4.5
关于c# - 跟踪 c#/.NET 任务流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24468894/