EndInvoke更改当前的CallContext-为什么? [英] EndInvoke changes current CallContext - why?

查看:52
本文介绍了EndInvoke更改当前的CallContext-为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下测试

[Test]
public void aaa()
{
    CallContext.LogicalSetData("aa", "1");

    Action parallelMethod = () => CallContext.LogicalSetData("aa", "2"); 
    var r = parallelMethod.BeginInvoke(null, null); 
    parallelMethod.EndInvoke(r);

    Assert.That(CallContext.LogicalGetData("aa"), Is.EqualTo("1")); 
}

谁能告诉我为什么该测试在最后一行失败?

Can anyone tell me why this test is failing on last line?

实际上,我知道为什么-因为EndInvoke正在将CallContext从paralell方法合并到当前方法-但我不明白这样做的原因.

Actually I know why - because EndInvoke is merging CallContext from paralell method to current one - but I don't understand the reason for this.

对于我来说,此行为类似于从称为:-(

For me this behaviour is similiar to changing method parameter values from inside of method that is called :-(

编辑:我将代码示例更改为仅使用LogicalGetData和LogicalSetData.如您在其他问题中所见,我想传递一些数据到另一个线程,但我没想到EndInvoke()会用其他线程中更改的值覆盖我的值.

I've changed my code example to use only LogicalGetData and LogicalSetData. As you can see in my other question I want to pass some data to another thread but I didn't expected that EndInvoke() will override my values with those changed in other thread.

推荐答案

您的示例所说明的行为确实是设计使然. LogicalCallContext能够通过异步调用或.net远程调用双向流动.正如您所观察到的,当您调用EndInvoke时,子上下文的LogicalCallContext被合并回父上下文.这是有意的,因此远程方法的调用者可以访问由远程方法设置的任何值.如果需要,您可以使用此功能从孩子那里流回数据 .

The behavior illustrated by your example is indeed by design. The LogicalCallContext is able to flow bi-directionally through an async invocation or a .net remoting call. When you call EndInvoke, the child context's LogicalCallContext is merged back into the parent's, as you have observed. This is intentional, so that callers of remote methods can get access to any values set by the remote method. You can use this feature to flow data back from the child, if you'd like.

借助.NET Framework源代码步进对其进行调试,对此有明确的注释:

Debugging this with the help of the .NET Framework source stepping, there are explicit comments to this effect:

在System.Runtime.Remoting.Proxies.RemotingProxy.Invoke中:

in System.Runtime.Remoting.Proxies.RemotingProxy.Invoke:

    case Message.EndAsync: 
         // This will also merge back the call context
         // onto the thread that called EndAsync
         RealProxy.EndInvokeHelper(m, false);

在System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper中:

in System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper:

    // Merge the call context back into the thread that
    // called EndInvoke 
    CallContext.GetLogicalCallContext().Merge(
         mrm.LogicalCallContext);

如果要避免合并数据,则很容易跳过,只需避免从主线程调用EndInvoke.例如,您可以使用ThreadPool.QueueUserWorkItem,它将使LogicalCallContext 输入,但不能流出,或者从AsyncCallback调用EndInvoke.

If you want to avoid having the data merge, it's pretty easy to skip, just avoid calling EndInvoke from the main thread. You could for example use ThreadPool.QueueUserWorkItem, which will flow the LogicalCallContext in but not out, or call EndInvoke from an AsyncCallback.

看一下Microsoft Connect站点上的示例,看不到LogicalSetData值从RunWorkerCompleted调用中流回的原因是BackgroundWorker没有将上下文流回.另外,请记住LogicalSetData与线程本地存储不同,因此RunWorkerCompleted恰好在UI线程上运行无关紧要-LogicalCallContext仍然有子上下文,除非父级显式地将其返回通过从生成线程调用EndInvoke,它将被放弃.如果需要线程本地存储,则可以从Thread进行访问,就像这样:

Looking at the example on the Microsoft Connect site, the reason that you're not seeing the LogicalSetData value get flowed back from the RunWorkerCompleted call is that BackgroundWorker does not flow the context back. Also, remember that LogicalSetData is not the same as thread-local storage, so it doesn't matter that RunWorkerCompleted happens to be running on the UI thread -- the LogicalCallContext there is still a child context, and unless the parent explicitly flows it back by calling EndInvoke from the spawning thread, it will be abandoned. If you want thread-local storage, you can access that from Thread, like so:

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Thread.SetData(Thread.GetNamedDataSlot("foo"), "blah!!");
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var val = (string)Thread.GetData(Thread.GetNamedDataSlot("foo"));
        MessageBox.Show(val ?? "no value");
    }

此示例弹出一个显示"blah !!"的消息框.原因是两个回调都在UI线程上运行,因此可以访问相同的线程本地存储.

This example pops a MessageBox displaying "blah!!". The reason is that both callbacks run on the UI thread, so have access to the same thread-local store.

希望这有助于清除问题.

Hope this helps clear things up.

这篇关于EndInvoke更改当前的CallContext-为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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