See*_*eeR 6 .net multithreading
我有以下测试
[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"));
}
Run Code Online (Sandbox Code Playgroud)
谁能告诉我为什么这个测试在最后一行失败?
其实我知道为什么 - 因为EndInvoke正在将paraConll方法的CallContext合并到当前的方法 - 但我不明白这个的原因.
对我来说,这种行为类似于从方法内部更改方法参数值:-(
编辑:我已经将我的代码示例更改为仅使用LogicalGetData和LogicalSetData.正如您在我的其他问题中所看到的,我想将一些数据传递给另一个线程,但我没想到EndInvoke()会覆盖我在其他线程中更改的值.
您的示例所示的行为确实是设计的.LogicalCallContext能够通过异步调用或.net远程调用双向流动.当您调用EndInvoke时,子上下文的LogicalCallContext将合并回父级,如您所观察到的那样.这是有意的,因此远程方法的调用者可以访问远程方法设置的任何值.您可以使用此功能将流数据回从孩子,如果你愿意的话.
在.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);
Run Code Online (Sandbox Code Playgroud)
在System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper中:
// Merge the call context back into the thread that
// called EndInvoke
CallContext.GetLogicalCallContext().Merge(
mrm.LogicalCallContext);
Run Code Online (Sandbox Code Playgroud)
如果你想避免数据合并,那么很容易跳过,只是避免从主线程调用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");
}
Run Code Online (Sandbox Code Playgroud)
这个例子弹出一个显示"blah !!"的MessageBox.原因是两个回调都在UI线程上运行,因此可以访问相同的线程本地存储.
希望这有助于澄清事情.
归档时间: |
|
查看次数: |
1372 次 |
最近记录: |