我有下面的测试
[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从paralell方法合并到当前的方法--但我不明白原因。
对我来说,这种行为类似于从方法内部更改方法参数值,称为-(
编辑:--我已经将代码示例更改为只使用LogicalGetData和LogicalSetData。正如您在我的另一个问题中所看到的,我想将一些数据传递给另一个线程,但我没想到EndInvoke()会用其他线程中更改的值覆盖我的值。
发布于 2009-09-24 16:33:33
你的例子所说明的行为确实是由设计造成的。LogicalCallContext能够双向地通过异步调用或.net远程调用进行流。当您调用EndInvoke时,子上下文的LogicalCallContext将被合并回父上下文,正如您所观察到的。这是有意的,因此远程方法的调用方可以访问由remote方法设置的任何值。如果您愿意的话,您可以使用此特性将数据从子节点返回。
在.NET框架源代码步进的帮助下调试这个过程,有一些显式的注释:
在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站点上的示例,您看不到LogicalSetData值从RunWorkerCompleted调用返回的原因是BackgroundWorker没有返回上下文。另外,请记住,LogicalSetData与线程是不同的--本地存储,所以RunWorkerCompleted碰巧运行在UI线程上并不重要-- LogicalCallContext仍然有一个子上下文,除非父线程通过从生成线程调用EndInvoke显式地返回它,否则它将被放弃。如果您想要线程本地存储,可以从线程访问它,如下所示:
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线程上运行,因此可以访问同一个线程本地存储。
希望这能把事情弄清楚。
发布于 2009-05-19 15:56:21
这是因为您将SetData/GetData与LogicalSetData/LogicalGetData混合在一起。有一个文章,您可以更多地了解这两种方法之间的差异。这里的经验法则是始终将SetData与GetData结合使用,LogicalSetData与LogicalGetData结合使用。
此修改将使您的测试通过:
[Test]
public void aaa()
{
CallContext.SetData("aa", "1");
Action parallelMethod = () => CallContext.SetData("aa", "2");
var r = parallelMethod.BeginInvoke(null, null);
Assert.That(CallContext.GetData("aa"), Is.EqualTo("1"));
parallelMethod.EndInvoke(r);
Assert.That(CallContext.GetData("aa"), Is.EqualTo("1"));
}https://stackoverflow.com/questions/883486
复制相似问题