嵌套多线程操作跟踪 [英] Nested multithread operations tracing

查看:213
本文介绍了嵌套多线程操作跟踪的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个code一样

void ExecuteTraced(Action a, string message)
{
    TraceOpStart(message);
    a();
    TraceOpEnd(message);
}

回调(一)可能再次打电话ExecuteTraced,并在某些情况下,异步(通过线程池,BeginInvoke的,PLINQ等,所以我没有能力明确地标明经营范围)。我想跟踪嵌套所有操作(即使他们执行异步地)。所以,我需要最后得到跟踪逻辑调用上下文中运行的能力(可能有很多并发线程,所以不可能使用lastTraced静态字段)。

The callback (a) could call ExecuteTraced again, and, in some cases, asynchronously (via ThreadPool, BeginInvoke, PLINQ etc, so I've no ability to explicitly mark operation scope). I want to trace all operation nested (even if they perform asynchronously). So, I need the ability to get last traced operation inside logical call context (there may be a lot of concurrent threads, so it's impossible to use lastTraced static field).

目前是CallContext.LogicalGetData和CallContext.LogicalSetData,但不幸的是,LogicalCallContext更改传播回父上下文的EndInvoke()调用。更糟的是,这可能occure在任何时刻,如果EndInvoke会()被调用异步。 EndInvoke会改变当前CallContext中 - 为什么

There're CallContext.LogicalGetData and CallContext.LogicalSetData, but unfortunately, LogicalCallContext propagates changes back to the parent context as EndInvoke() called. Even worse, this may occure at any moment if EndInvoke() was called async. EndInvoke changes current CallContext - why?

此外,还有Trace.CorrelationManager,但是它基于CallContext中并具有所有相同的烦恼。

Also, there is Trace.CorrelationManager, but it based on CallContext and have all the same troubles.

有一个变通方法:使用CallContext.HostContext属性不传播早在异步操作结束。此外,它一点儿也不克隆,因此该值应该是不可变 - 不是一个问题。但是,它的使用HttpContext的,因此,解决办法是无法使用的Asp.Net应用程序。

There's a workaround: use the CallContext.HostContext property which does not propagates back as async operation ended. Also, it does'nt clone, so the value should be immutable - not a problem. Though, it's used by HttpContext and so, workaround is not usable in Asp.Net apps.

我看到的唯一办法是换HostContext(如果不是我的),或整个LogicalCallContext为动态和发送的所有呼叫旁边去年跟踪操作。

The only way I see is to wrap HostContext (if not mine) or entire LogicalCallContext into dynamic and dispatch all calls beside last traced operation.

推荐答案

好吧,我回答我自己。

短之一:有没有办法解决。

稍微详细的:

现在的问题是,我需要一种方法来按每个逻辑方​​面存储最后一个活动的操作。追踪code will've在执行流程无法控制,所以它不可能通过lastStartedOperation作为参数。呼叫上下文可以克隆(例如,如果另一个线程开始),所以我需要克隆的值作为背景克隆。

The problem is, I need a way to store last active operation per each logical context. Tracing code will've no control over execution flow, so it's impossible to pass lastStartedOperation as a parameter. Call context may clone (e.g. if another thread started), so I need to clone the value as context clones.

CallContext.LogicalSetData()适合,但是它的值合并到原来的上下文结束异步操作(实际上,更换前EndInvoke会叫的所有更改)。 Theortically,它的可以的occure即使不同步,给CallContext.LogicalGetData()的未predictable结果

CallContext.LogicalSetData() suits well, but it merges values into the original context as asynchronous operation ended (in effect, replacing all changes made before EndInvoke called). Theortically, it may occure even asynchronously, giving unpredictable result of CallContext.LogicalGetData().

我说,从理论上讲,因为简单的调用a.EndInvoke()一个AsyncCallback内不会在原来的背景下替代值。虽然,我没有检查远程调用的行为(这似乎,WCF根本不兑现CallContext中)。此外,文档(旧)说:

I say theoretically because simple call a.EndInvoke() inside an asyncCallback does not replace values in the original context. Though, I did not check behavior of remoting calls (and it seems, WCF does not honor CallContext at all). Also, the documentation (old one) says:

BeginInvoke方法传递   CallContext中到服务器。什么时候   EndInvoke会方法被调用,所述   CallContext中合并放回   线。这包括两种情况:   的BeginInvoke和EndInvoke被称为   顺序和其中的BeginInvoke是   叫上一个线程和EndInvoke是   叫上一个回调函数。

The BeginInvoke method passes the CallContext to the server. When EndInvoke method is called, the CallContext is merged back onto the thread. This includes cases where BeginInvoke and EndInvoke are called sequentially and where BeginInvoke is called on one thread and EndInvoke is called on a callback function.

最后一个版本是没​​有那么明确的:

Last version is not so definite:

BeginInvoke方法传递   CallContext中到服务器。当。。。的时候   EndInvoke会方法被调用,所述数据   包含在CallContext中的复制   返回到调用线程   BeginInvoke的。

The BeginInvoke method passes the CallContext to the server. When the EndInvoke method is called, the data contained in the CallContext is copied back onto the thread that called BeginInvoke.

如果你到Digg的框架源代码,你会发现,值实际上存储里面LogicalCallContext一个哈希表中的当前线程的当前执行上下文里面里面。

If you digg into framework source, you'll find that values are actually stored inside a hashtable inside LogicalCallContext inside current ExecutionContext of the current thread.

当调用上下文克隆(如上的BeginInvoke)LogicalCallContext.Clone调用。和EndInvoke(原CallContext中内至少调用时)调用LogicalCallContext.Merge()替换旧值内m_Datastore用新的。

When call context clones (e.g. on BeginInvoke) LogicalCallContext.Clone called. And EndInvoke (at least when called inside original CallContext) calls LogicalCallContext.Merge() replacing old values inside m_Datastore with new ones.

因此​​,我们需要以某种方式提供合并回将被克隆而不是价值。

So we need somehow supply the value which will be cloned but not merged back.

LogicalCallContext.Clone()也克隆(不合并)两个私人领域,m_RemotingData和m_SecurityData的内容。作为该领域的类型定义为内部,你不能从他们那里获得(即使有EMIT),增加财产MyNoFlowbackValue和替换m_RemotingData(或另一个)字段的值与派生类的实例。

LogicalCallContext.Clone() also clones (without merging) content of two private fields, m_RemotingData and m_SecurityData. As the field's types defined as internal, you could not derive from them (even with emit), add property MyNoFlowbackValue and replace m_RemotingData (or another one) field's value with instance of derived class.

此外,字段的类型是不是从MBR派生,所以不可能使用透明代理来包装他们。

Also, field's types are not derived from MBR, so it's impossible to wrap them using transparent proxy.

您无法从LogicalCallContext继承 - 它是密封的。 (注:实际上,你可以 - 。如果使用CLR分析API,以取代IL作为模拟框架不期望的解决方案)

You could not inherit from LogicalCallContext - it's sealed. (N.B. actually, you could - if using CLR profiling api to replace IL as mock frameworks do. Not a desired solution.)

您不能取代m_Datastore价值,因为LogicalCallContext序列化哈希表,而不是哈希表本身的唯一内容。

You could not replace the m_Datastore value, because LogicalCallContext serializes only content of the hashtable, not the hashtable itself.

最后的解决方案是使用CallContext.HostContext。这有效地存储在LogicalCallContext的m_hostContext场数据。 LogicalCallContext.Clone()的股票(未克隆)m_hostContext的价值,所以价值应该是一成不变的。不是一个问题,但。

Last solution is to use CallContext.HostContext. This effectively stores data in the m_hostContext field of the LogicalCallContext. LogicalCallContext.Clone() shares (not clones) the value of m_hostContext, so the value should be immutable. Not a problem though.

即使失败,如果HttpContext的使用,因为它设置Call​​Context.HostContext财产更换旧的价值。讽刺的是,HttpContext的不实现ILogicalThreadAffinative,因此不存储为m_hostContext字段的值。它只是替换为空旧值。

And even this fails if HttpContext used, as it sets CallContext.HostContext property replacing your old value. Ironically, HttpContext does not implement ILogicalThreadAffinative, and therefore does not stored as value of the m_hostContext field. It just replaces old value with null.

那么,有没有办法解决也绝不会,因为CallContext中是远程的一部分,远程已经过时了。

So, there's no solution and never will be, as CallContext is the part of remoting and remoting is obsolete.

P.S。 Thace.CorrelationManager使用CallContext中在内部,因此不工作,需要的话,也。顺便说一句,LogicalCallContext有特殊的解决方法克隆CorrelationManager的操作堆栈环境的克隆。可悲的是,这对合并没有特殊的解决办法。完美!

P.S. Thace.CorrelationManager uses CallContext internally and therefore does not work as desired, too. BTW, LogicalCallContext has special workaround to clone CorrelationManager's operation stack on context clone. Sadly, it has not special workaround on merge. Perfect!

P.P.S。示例:

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();
}

这篇关于嵌套多线程操作跟踪的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆