当前位置: 首页 > 编程语言 > C#

阅读C#中的介绍——如何防范?分享

时间:2023-04-10 14:43:41 C#

看C#中的介绍——如何防范?MSDN杂志中的一篇文章讨论了阅读简介的概念,并给出了一个可以被它破坏的代码示例。publicclassReadIntro{privateObject_obj=newObject();voidPrintObj(){对象obj=_obj;if(obj!=null){Console.WriteLine(obj.ToString());//可能抛出NullReferenceException}}voidUninitialize(){_obj=null;注意“可能会抛出NullReferenceException”评论——我从来不知道这是可能的。所以我的问题是:如何防止阅读介绍?我也非常感谢编译器决定在读取时引入精确解释,因为这篇文章没有涵盖它。让我试着通过分解来澄清这个复杂的问题。什么是“读简介”?《阅读介绍》是一段优化代码:publicstaticFoofoo;//我可以在另一个线程上改变!voidDoBar(){FoofooLocal=foo;如果(fooLocal!=null)fooLocal.Bar();}通过消除局部变量来优化。编译器可以fooLocal,如果只有一个线程,那么foo和fooLocal是一样的。明确允许编译器进行任何在单线程上不可见的优化,即使它在多线程场景中变得可见。因此允许编译器将其重写为:voidDoBar(){if(foo!=null)foo.Bar();现在有一个竞争条件。如果foo在检查后从非null变为null,它可能会第二次读取foo,它可能第二次为null,然后崩溃。从诊断碰撞桩的人的角度来看,这将是完全神秘的。这真的会发生吗?正如您链接的文章所说:请注意,您将无法在x86-x64上的.NETFramework4.5中使用此代码示例重现NullReferenceException。在.NETFramework4.5中,阅读简介更难重现,但在某些特殊情况下仍然会发生。x86/x64芯片有一个“强大”的内存模型,jit编译器在这方面并不激进;他们不做这个优化。如果您碰巧在内存模型较弱的处理器(例如ARM芯片)上运行代码,那么一切都将失败。当你说“编译器”时,你指的是哪个编译器?我的意思是jit编译器。C#编译器从不以这种方式引入读取。(这是允许的,但实际上从来没有。)在没有内存屏障的情况下在线程之间共享内存不是一种不好的做法吗?是的。这里应该做一些事情来引入内存屏障,因为foo的值可能已经是处理器缓存中的陈旧缓存值。我偏向于引入内存屏障是使用锁。您还可以使字段可变,或使用VolatileRead,或使用其中一种Interlocked方法。所有这些都引入了内存障碍。(易失性仅引入“半栅栏”仅供参考。)仅仅因为存在内存屏障并不一定意味着不执行读取以引入优化。然而,抖动在追求影响包含内存屏障的代码的优化方面远没有那么激进。这种模式还有其他危险吗?当然!我们假设没有阅读介绍。你仍然有竞争条件。如果另一个线程在检查后将foo设置为null,并且还修改了Bar将使用的全局状态怎么办?现在你有两个线程,其中一个认为foo不为null并且调用Bar的全局状态正常,另一个线程认为相反,你正在运行Bar。这是灾难的根源。那么这里的最佳做法是什么?首先,不要跨线程共享内存。在程序的主线中拥有两个控制线程的整个想法从一开始就很疯狂。它不应该是一件事。使用线程作为轻量级进程;给他们一个独立的任务来执行,完全不与程序的主线内存交互,只用它们来解决计算密集型工作。其次,如果要跨线程共享内存,则使用锁序列化对该内存的访问。如果他们没有争用,那么锁很便宜,如果你有争用,那么就解决这个问题。众所周知,低锁和无锁解决方案很难获得。第三,如果您要在线程之间共享内存,则您调用的每个涉及共享内存的方法都必须对竞争条件具有鲁棒性,否则必须消除竞争。这是一个沉重的负担,这就是为什么你一开始就不应该在那里。我的观点是:阅读介绍是可怕的,但坦率地说,如果您编写的代码可以巧妙地在线程之间共享内存,那么它们是您最不用担心的。首先还有一千零一件事要担心。您不能真正“保护”读取导入,因为它是编译器优化(使用Debug构建时除外,这当然不会优化)。值得记录的是,优化器将维护函数的单线程语义,如文章所述,这可能会在多线程情况下导致问题。也就是说,我对他的例子感到困惑。在JeffreyRichter的书CLRviaC#(在本例中为v3)中,他在事件部分介绍了这种模式,并指出在上面的示例片段中,它在理论上不起作用。然而,这是Microsoft在.Net存在时早期推荐的模式,因此他采访的JIT编译器人员表示,他们必须确保片段永不中断。(他们总是有可能因为某种原因决定它值得打破——我认为EricLippert可以说明这一点)。最后,与文章不同,Jeffrey提供了在多线程情况下处理这种情况的“正确”方法(我用示例代码修改了他的示例):Objecttemp=Interlocked.CompareExchange(ref_obj,null,null);如果(temp!=null){Console.WriteLine(temp.ToString());我只是浏览了这篇文章,但似乎作者正在寻找的是您需要将_obj成员声明为volatile。以上就是C#学习教程:看了C#中的介绍——如何防范?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: