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

空合并运算符是线程安全的吗?share

时间:2023-04-10 14:48:48 C#

nullcoalesceoperator线程安全吗?所以这就是问题的症结所在:Foo.Bar可以返回null吗?澄清一下,'_bar'是否可以在被评估为非空值并返回之前设置为空值?公共类Foo{对象_bar;公共对象栏{get{return_bar??新对象();}设置{_bar=值;}}}我知道使用下面的get方法是不安全的,并且可以返回空值:get{return_bar!=null?_bar:新对象();更新:另一种看待同一问题的方式,这个例子可能更清楚:publicstaticTGetValue(refTvalue)whereT:class,new(){returnvalue??新T();再次请求GetValue(...)会返回null吗?根据您的定义,这可能是线程安全的,也可能不是线程安全的……我想正确的问题陈述是在询问它是否是对值的原语操作……DavidYaw通过说上述函数等同于以下内容:publicstaticTGetValue(refTvalue)whereT:class,new(){Tresult=value;如果(结果!=null)返回结果;否则返回新的T();不,这不是线程安全的。上述IL编译为:.methodpublichidebysigspecialnameinstanceobjectget_Bar()cilmanaged{.maxstack2.localsinit([0]objectCS$1$0000)L_0000:nopL_0001:ldarg.0L_0002:ldfldobjectConsoleApplication1.Program/MainClass::_barL_0007:dupL_0008:brtrue.sL_0010L_000a:popL_000b:newobjinstancevoid[mscorlib]System.Object::.ctor()L_0010:stloc.0L_0011:br.sL_0013L_0013:ldloc.0L_0014:ret}这有效地加载了_bar字段,然后检查它是否存在,并跳转到末尾。没有同步,并且由于这是多个IL指令,辅助线程可能会导致竞争条件-导致返回的对象与一组不同。通过Lazy来处理惰性实例化会好很多。这提供了一个线程安全的惰性实例化模式。当然,上面的代码并没有进行惰性实例化(而是每次都返回一个新对象,直到某个时间设置了_bar),但我怀疑这是一个错误而不是预期的行为。另外,Lazy使设置变得困难。要以线程安全的方式复制上述行为,需要显式同步。至于您的更新:Bar属性的getter从不返回null。查看上面的IL,它是_bar(通过ldfld),然后使用brtrue.s检查该对象是否为空。如果对象不为null,则跳转,通过stloc.0将_bar的值从执行栈复制到本地,返回——返回实际值的_bar。如果_bar设置_bar,它会从执行堆栈中弹出,创建一个新对象,然后存储并返回。这两种情况都阻止返回空值。但是,我通常不认为这是线程安全的,因为调用set可以在调用get的同时调用,这可能会导致返回不同的对象,并且返回实例(设置值或新对象)是一种竞争条件.我不会使用“线程安全”这个词来指代它。相反,我会问这个问题,它与空合并运算符相同吗?得到{return_bar!=null?_bar:新对象();}或获取{Objectresult=_bar;如果(结果==null){结果=新对象();}返回结果;通过阅读其他回复,请参阅它似乎编译为等同于第二个,而不是第一个。如您所知,第一个可以返回null,但第二个永远不会。这个线程安全吗?从技术上讲,没有。在读取_bar时,另一个线程可以修改_bar,getter将返回一个过时的值。但从你问的方式来看,我认为这正是你要找的。编辑:这是避免整个问题的方法。由于value是局部变量,因此无法在后台更改。公共类Foo{Object_bar=newObject();公共对象栏{get{return_bar;}设置{_bar=值??新对象();}}}编辑2:这是我从发布编译IL中看到的,以及我对IL的解释。.methodpublichidebysigspecialnameinstanceobjectget_Bar_NullCoalesce()cilmanaged{.maxstack8L_0000:ldarg.0//将参数0加载到堆栈上(我不知道参数0是什么,我不明白这个语句。)L_0001:ldfldobjectCoalesceTest::_bar//将对_bar的引用加载到堆栈上。L_0006:dup//复制堆栈上的值。L_0007:brtrue.sL_000f//如果堆栈中的值不为零,则跳转到L_000f。//我相信这会消耗堆栈顶部的值,将ldfld的原始结果作为堆栈中的唯一内容。L_0009:pop//从堆栈中移除ldfld的结果。L_000a:newobjinstancevoid[mscorlib]System.Object::.ctor()//创建一个新对象,将对它的引用放在堆栈上。L_000f:ret//返回堆栈顶部的任何内容。}这是我从其他方式看到的:.methodpublichidebysigspecialnameinstanceobjectget_Bar_IntermediateResultVar()cilmanaged{.maxstack1.localsinit([0]obj等结果)L_0000:ldarg.0L_0001:ldfld对象CoalesceTest::_barL_0006:stloc.0L_0007:ldloc.0L_0008:brtrue.sL_0010L_000a:newobj实例void[mscorlib]System.Object::.ctor()L_000f:stloc.0L_0010:ldloc.0L_0011:ret}.methodpublichidebysigspecialnameinstanceobjectget_Bar_TrinaryOperator()cilmanaged{.maxstack8L_0000:ldarg.0L_0001:ldfldobjectCoalesceTest::_barL_000_L_0006:Lnewobjinstancevoid[mscorlib]System.Object::.ctor()L_000d:retL_000e:ldarg.0L_000f:ldfldobjectCoalesceTest::_barL_0014:ret}在IL中,很明显是用三元运算符读取了两次取_bar字段,但只有一次与nullcoalesce和中间结果var同样,零合并方法的IL非常接近中间结果var方法。这是我用来生成这些的源代码:publicobjectBar_NullCoalesce{get{returnthis._bar??新对象();}}publicobjectBar_IntermediateResultVar{get{objectresult=this._bar;if(result==null){result=newObject();}返回结果;}}publicobjectBar_TrinaryOperator{get{returnthis._bar!=null?this._bar:新对象();}}getter永远不会返回null。这是因为当对变量(_bar)执行读取时,将计算表达式,然后生成的对象(或null)将_bar“释放”变量(_bar)。这是第一次评估的结果,然后“传递”给合并运算符。(请参阅Reed对IL的出色回答。)但是,这不是线程安全的,并且由于与上述相同的原因,分配很容易丢失。反射器说不:Listl=null;变量x=l??新列表();编译为:[STAThread]publicstaticvoidMain(string[]args){Listlist=null;如果(列表==null){新列表();就您提到的方面而言,这似乎不是线程安全的。以上是C#学习教程:nullcoalesceoperator线程安全吗?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: