views:

472

answers:

7

Everyone knows that this is not thread safe:

public StringBuilder Builder
{
    get 
    {
        if (_builder != null)
            _builder = new StringBuilder();
        return _builder; 
    }
}

What about this?

public StringBuilder Builder
{
    get { return _builder ?? (_builder = new StringBuilder()); }
}
+7  A: 

NO for both versions

van
+8  A: 

That is no more or less thread-safe; you could still have two threads do the null check at the same time, thus create separate objects and not see the other.

Marc Gravell
What's your opinion of using Interlocked.CompareExchange( ref _builder, new StringBuilder(), null )?
LBushkin
+7  A: 

BEGIN EDIT

Based on your edited title, the null-coalescing operator itself seems to be thread-safe (see Phil Haack's analysis). It appears, however, that it doesn't guarantee against the potential multiple calls to the StringBuilder constructor.

END EDIT

You have a larger problem with threading, and that is that the Builder property itself represents state that can be shared across threads. Even if you make the lazy initialization thread safe, there's no guarantee that methods consuming Builder are doing it in a thread safe manner.

// below code makes the getter thread safe
private object builderConstructionSynch = new object();
public StringBuilder Builder
{
    get
    {
        lock (builderConstructionSynch)
        {
            if (_builder == null) _builder = new StringBuilder();
        }
        return _builder;
    }
}

The above will prevent the threading problem in the lazy initialization of _builder, but unless you synchronize your calls to instance methods of StringBuilder, you're not guaranteed thread safety in any methods that consume the Builder property. This is because instance methods in StringBuilder weren't designed to be thread safe. See the below text from the MSDN StringBuilder page.

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

If you're consuming StringBuilder in multiple threads, you're probably better served encapsulating it in your class. Make Builder private and expose what behavior you need as a public method:

public void AppendString(string toAppend)
{
    lock (Builder)
    {
        Builder.Append(toAppend);
    }
}

This way you're not writing synchronization code all over the place.

Michael Meadows
I just thought that ?? is atomic operation. ? is not thread safe too?
ArsenMkrt
I can't say whether or not the null-coalescing operator is atomic, but my assertion is that you have a bigger problem since StringBuilder is not intrinsically thread-safe.
Michael Meadows
See edited answer for answer about null-coalescing operator's thread safety (credit to Phil Haack). It's thread safe inasmuch as it doesn't create race conditions, but you could potentially end up with two separate instances of Builder if conditions are perfect.
Michael Meadows
Ok thanks Michael, good answer!
ArsenMkrt
+1  A: 

No, neither are atomic

AgileJon
+1  A: 

The answers given are correct, both are not threadsafe. In fact, they are mostly equivalent, and the ?? operator is just compiler magic to make the code leaner. You need to use some synchronization mechanism if you want this to become threadsafe.

Lucero
A: 

If you plan to do a lot of multithreaded development I would suggest you read this book:

http://www.amazon.com/Concurrent-Programming-Windows-Microsoft-Development/dp/032143482X/ref=sr_1_1?ie=UTF8&s=books&qid=1244035846&sr=8-1

Simon H.
+1  A: 

I have not tested this approach myself, but if you want thread safety without the overhead of a locking scheme and you aren't worried about potentially creating and discarding an object instance, you could try this:

using System.Threading;

public StringBuilder Builder
{
    get 
    {
        if (_builder != null)
            Interlocked.CompareExchange( ref _builder, new StringBuilder(), null );
        return _builder; 
    }
}

The call to CompareExchange() will perform an atomic replacement of the value in _builder with a new instance of StringBuilder only if _builder == null. All of the methods on the Interlocked class are gauranteed to NOT be preempted by thread switches.

LBushkin
BTW, it's probably a bad idea to share an instance of a StringBuilder accross threads. SB is inherently not thread safe, and it's unclear, even if it were, that you could do anything meaningful with it across threads that are not synchronized.
LBushkin