Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is .NET's StringBuilder thread-safe

The regular "Thread Safety" section of the MSDN documentation for StringBuilder states that:

...any instance members are not guaranteed to be thread safe...

but this statement feels like it has been copied and pasted for almost every class in the Framework:

http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx

However, these blog posts by Gavin Pugh mention thread-safe behaviours of StringBuilder:

http://www.gavpugh.com/2010/03/23/xnac-stringbuilder-to-string-with-no-garbage/

http://www.gavpugh.com/2010/04/01/xnac-avoiding-garbage-when-working-with-stringbuilder/

Additionally, the source of StringBuilder revealed by Reflector, and the accompanying comments in the SSCLI source, also suggest many implementation considerations to ensure thread-safety:

http://labs.developerfusion.co.uk/SourceViewer/browse.aspx?assembly=SSCLI&namespace=System.Text&type=StringBuilder

Does anyone have any more insight into whether a StringBuilder instance is safe to share among multiple concurrent threads?

like image 786
Jason Stangroome Avatar asked Jan 12 '12 07:01

Jason Stangroome


People also ask

Is StringBuilder thread-safe?

StringBuilder is non-synchronized i.e. not thread safe. It means two threads can call the methods of StringBuilder simultaneously. StringBuffer is less efficient than StringBuilder. StringBuilder is more efficient than StringBuffer.

Is StringBuilder thread-safe C#?

StringBuilder is also not thread-safe, so operating on it with concurrent threads is illegal.

Is StringBuilder append thread-safe?

StringBuilder is compatible with StringBuffer API but with no guarantee of synchronization. Because it's not a thread-safe implementation, it is faster and it is recommended to use it in places where there's no need for thread safety.

Which class is not thread-safe?

When designing a class that may be used for concurrent programming—that is, a class whose instances may be used by more than one thread at a time—it is imperative that you make sure the class is " thread-safe.” Consider the IntList class of Example 2-7. This class is not thread safe.


2 Answers

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); 
like image 155
Marc Gravell Avatar answered Sep 21 '22 15:09

Marc Gravell


The whole point of the documentation is to give you guarantees. In this case on instance members nothing is guaranteed to be thread-safe and you should treat it as such, therefore relying on external synchronisation methods.

That some things may be threadsafe is an implementation detail which can and maybe does change from one version of the framework to the next or from one implementation to the next (in fact there are plenty of such details changing in framework versions; Eric Lippert has some posts detailing a few of them). Don't rely on it.

(In other words: Don't write code to an implementation, write it against the interface and contract which is the metadata of the class and its documentation in this case.)

like image 30
Joey Avatar answered Sep 19 '22 15:09

Joey