Is .NET’s StringBuilder thread-safe

Absolutely not; here’s a simple example lifted from 4.0 via reflector:

[SecuritySafeCritical]
public StringBuilder Append(char value)
{
    if (this.m_ChunkLength < this.m_ChunkChars.Length)
    {
        this.m_ChunkChars[this.m_ChunkLength++] = value;
    }
    else
    {
        this.Append(value, 1);
    }
    return this;
}

The attribute just handles callers, not thread-safety; this is absolutely not thread-safe.

Update: looking at the source he references, this is clearly not the current .NET 4.0 code-base (comparing a few methods). Perhaps he is talking about a particular .NET version, or maybe XNA – but it is not the case in general. The 4.0 StringBuilder does not have a m_currentThread field, which Gavin’s source material uses; there’s a hint (an unused constant ThreadIDField) that it used to exist, but… no longer.


If you want a direct disproof – run this on 4.0; it will most likely give the wrong length (I’ve seen a few in the 4k region, a few in the 2k region – it should be exactly 5000), but some other Append methods (Append(char) for example) tend more likely to throw exceptions, depending on timing:

var gate = new ManualResetEvent(false);
var allDone = new AutoResetEvent(false);
int counter = 0;
var sb = new StringBuilder();
ThreadStart work = delegate
{
    // open gate when all 5 threads are running
    if (Interlocked.Increment(ref counter) == 5) gate.Set();
    else gate.WaitOne();

    for (int i = 0; i < 1000; i++) sb.Append("a");

    if (Interlocked.Decrement(ref counter) == 0) allDone.Set();
};
for(int i = 0 ; i < 5 ; i++)
{
    new Thread(work).Start();
}
allDone.WaitOne();
Console.WriteLine(sb.Length);

Leave a Comment