Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
287 views
in Technique[技术] by (71.8m points)

c# - Is the null coalesce operator thread safe?

So this is the meat of the question: Can Foo.Bar ever return null? To clarify, can '_bar' be set to null after it's evaluated as non-null and before it's value is returned?

    public class Foo
    {
        Object _bar;
        public Object Bar
        {
            get { return _bar ?? new Object(); }
            set { _bar = value; }
        }
    }

I know using the following get method is not safe, and can return a null value:

            get { return _bar != null ? _bar : new Object(); }

UPDATE:

Another way to look at the same problem, this example might be more clear:

        public static T GetValue<T>(ref T value) where T : class, new()
        {
            return value ?? new T();
        }

And again asking can GetValue(...) ever return null? Depending on your definition this may or may not be thread-safe... I guess the right problem statement is asking if it is an atomic operation on value... David Yaw has defined the question best by saying is the above function the equivalent to the following:

        public static T GetValue<T>(ref T value) where T : class, new()
        {
            T result = value;
            if (result != null)
                return result;
            else
                return new T();
        }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

No, this is not thread safe.

The IL for the above compiles to:

.method public hidebysig specialname instance object get_Bar() cil managed
{
    .maxstack 2
    .locals init (
        [0] object CS$1$0000)
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldfld object ConsoleApplication1.Program/MainClass::_bar
    L_0007: dup 
    L_0008: brtrue.s L_0010
    L_000a: pop 
    L_000b: newobj instance void [mscorlib]System.Object::.ctor()
    L_0010: stloc.0 
    L_0011: br.s L_0013
    L_0013: ldloc.0 
    L_0014: ret 
}

This effectively does a load of the _bar field, then checks its existence, and jumps ot the end. There is no synchronization in place, and since this is multiple IL instructions, it's possible for a secondary thread to cause a race condition - causing the returned object to differ from the one set.

It's much better to handle lazy instantiation via Lazy<T>. That provides a thread-safe, lazy instantiation pattern. Granted, the above code is not doing lazy instantiation (rather returning a new object every time until some time when _bar is set), but I suspect that's a bug, and not the intended behavior.

In addition, Lazy<T> makes setting difficult.

To duplicate the above behavior in a thread-safe manner would require explicit synchronization.


As to your update:

The getter for the Bar property could never return null.

Looking at the IL above, it _bar (via ldfld), then does a check to see if that object is not null using brtrue.s. If the object is not null, it jumps, copies the value of _bar from the execution stack to a local via stloc.0, and returns - returning _bar with a real value.

If _bar was unset, then it will pop it off the execution stack, and create a new object, which then gets stored and returned.

Either case prevents a null value from being returned. However, again, I wouldn't consider this thread-safe in general, since it's possible that a call to set happening at the same time as a call to get can cause different objects to be returned, and it's a race condition as which object instance gets returned (the set value, or a new object).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...