Sin*_*nix 4 .net multithreading trace operations
我的代码相似
void ExecuteTraced(Action a, string message)
{
TraceOpStart(message);
a();
TraceOpEnd(message);
}
Run Code Online (Sandbox Code Playgroud)
回调(a)可以再次调用ExecuteTraced,在某些情况下,异步调用(通过ThreadPool,BeginInvoke,PLINQ等,因此我无法明确标记操作范围).我想跟踪嵌套的所有操作(即使它们异步执行).所以,我需要能够在逻辑调用上下文中获得最后的跟踪操作(可能有很多并发线程,因此不可能使用lastTraced静态字段).
有CallContext.LogicalGetData和CallContext.LogicalSetData,但不幸的是,LogicalCallContext在调用EndInvoke()时将更改传播回父上下文.更糟糕的是,如果EndInvoke()被称为异步,这可能随时发生. EndInvoke更改当前的CallContext - 为什么?
此外,还有Trace.CorrelationManager,但它基于CallContext并且具有所有相同的麻烦.
有一种解决方法:使用CallContext.HostContext属性,该属性在异步操作结束时不会传播回来.此外,它没有克隆,所以值应该是不可变的 - 不是问题.虽然,它被HttpContext使用,因此,解决方法在Asp.Net应用程序中不可用.
我看到的唯一方法是将HostContext(如果不是我的)或整个LogicalCallContext包装成动态并在最后一个跟踪操作旁边调度所有调用.
好的,我正在回答自己.
简短的一句:没有解决方案.
稍微详细一点:
问题是,我需要一种方法来存储每个逻辑上下文的最后一个活动操作.跟踪代码无法控制执行流程,因此无法将lastStartedOperation作为参数传递.调用上下文可以克隆(例如,如果另一个线程启动),所以我需要将值克隆为上下文克隆.
CallContext.LogicalSetData()适合很好,但它合并值到原来的上下文结束异步操作(实际上,更换EndInvoke会调用之前所做的所有更改).从理论上讲,它甚至可能异步发生,从而产生CallContext.LogicalGetData()的不可预测的结果.
我在理论上说,因为在asyncCallback中简单调用a.EndInvoke()不会替换原始上下文中的值.虽然,我没有检查远程调用的行为(似乎,WCF根本不尊重CallContext).此外,文档(旧文档)说:
BeginInvoke方法将CallContext传递给服务器.调用EndInvoke方法时,CallContext将合并回线程.这包括顺序调用BeginInvoke和EndInvoke以及在一个线程上调用BeginInvoke并在回调函数上调用EndInvoke的情况.
最后版本不是那么明确:
BeginInvoke方法将CallContext传递给服务器.调用EndInvoke方法时,CallContext中包含的数据将被复制回名为BeginInvoke的线程.
如果您深入了解框架源代码,您会发现值实际存储在当前线程的当前ExecutionContext内的LogicalCallContext内的哈希表中.
当调用上下文克隆(例如,在BeginInvoke上)时调用LogicalCallContext.Clone.EndInvoke(至少在原始CallContext中调用时)调用LogicalCallContext.Merge()用新的值替换m_Datastore中的旧值.
因此,我们需要以某种方式提供将被克隆但未合并的价值.
LogicalCallContext.Clone()还克隆(不合并)两个私有字段m_RemotingData和m_SecurityData的内容.由于字段的类型定义为内部,您无法从它们派生(即使使用emit),添加属性MyNoFlowbackValue并将m_RemotingData(或另一个)字段的值替换为派生类的实例.
此外,字段的类型不是从MBR派生的,因此不可能使用透明代理来包装它们.
你不能从LogicalCallContext继承 - 它是密封的.(实际上,你可以 - 如果使用CLR profiling api来替换IL作为模拟框架那么做.不是一个理想的解决方案.)
您无法替换m_Datastore值,因为LogicalCallContext仅序列化哈希表的内容,而不是哈希表本身.
最后的解决方案是使用CallContext.HostContext.这有效地将数据存储在LogicalCallContext的m_hostContext字段中.LogicalCallContext.Clone()共享(而不是克隆)m_hostContext的值,因此该值应该是不可变的.不过不是问题.
如果使用HttpContext,甚至会失败,因为它设置CallContext.HostContext属性来替换旧值.具有讽刺意味的是,HttpContext没有实现ILogicalThreadAffinative,因此不会存储为m_hostContext字段的值.它只是将旧值替换为null.
因此,没有解决方案也永远不会,因为CallContext是远程处理和远程处理的一部分已经过时了.
PS Thace.CorrelationManager在内部使用CallContext,因此也不能按预期工作.BTW,LogicalCallContext有特殊的解决方法来克隆上下文克隆上的CorrelationManager操作堆栈.可悲的是,它没有关于合并的特殊解决方法.完善!
PPS样本:
static void Main(string[] args)
{
string key = "aaa";
EventWaitHandle asyncStarted = new AutoResetEvent(false);
IAsyncResult r = null;
CallContext.LogicalSetData(key, "Root - op 0");
Console.WriteLine("Initial: {0}", CallContext.LogicalGetData(key));
Action a = () =>
{
CallContext.LogicalSetData(key, "Async - op 0");
asyncStarted.Set();
};
r = a.BeginInvoke(null, null);
asyncStarted.WaitOne();
Console.WriteLine("AsyncOp started: {0}", CallContext.LogicalGetData(key));
CallContext.LogicalSetData(key, "Root - op 1");
Console.WriteLine("Current changed: {0}", CallContext.LogicalGetData(key));
a.EndInvoke(r);
Console.WriteLine("Async ended: {0}", CallContext.LogicalGetData(key));
Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1343 次 |
| 最近记录: |