gpt4 book ai didi

.net - EndInvoke 更改当前 CallContext - 为什么?

转载 作者:行者123 更新时间:2023-12-04 14:53:13 24 4
gpt4 key购买 nike

我有以下测试

[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"));
}

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

其实我知道为什么 - 因为 EndInvoke 正在将 CallContext 从并行方法合并到当前方法 - 但我不明白这样做的原因。

对我来说,这种行为类似于从调用的方法内部更改方法参数值:-(

编辑:我已将代码示例更改为仅使用 LogicalGetData 和 LogicalSetData。正如你在我的另一个 question 中看到的我想将一些数据传递给另一个线程,但我没想到 EndInvoke() 会用其他线程中更改的值覆盖我的值。

最佳答案

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

在 .NET Framework 源代码步进的帮助下进行调试,对此效果有明确的注释:

在 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 中:
    // Merge the call context back into the thread that
// called EndInvoke
CallContext.GetLogicalCallContext().Merge(
mrm.LogicalCallContext);

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

查看 Microsoft Connect 站点上的示例,您没有看到 LogicalSetData 值从 RunWorkerCompleted 调用返回的原因是 BackgroundWorker 没有返回上下文。另外,请记住 LogicalSetData 与线程本地存储不同,因此 RunWorkerCompleted 恰好在 UI 线程上运行并不重要——LogicalCallContext 仍然有一个子上下文,除非父级明确地将其流回通过从生成线程调用 EndInvoke,它将被放弃。如果您想要线程本地存储,您可以从 Thread 访问它,如下所示:
    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");
}

本示例弹出一个 MessageBox,显示“blah!!”。原因是两个回调都在 UI 线程上运行,因此可以访问同一个线程本地存储。

希望这有助于澄清事情。

关于.net - EndInvoke 更改当前 CallContext - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/883486/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com