C# .NET - Is this threading code safe? - Asked By Patrick Skelton on 04-Mar-13 02:04 PM

Hi,

Just when you think you understand something ...

I recently wrote some code that I have distilled down to the example below.  Can anyone tell me if it is safe?  It appears to be.  I get the expected output (including two calls to construction of the StringBuilder).  But my machine is a single CPU/single core, so maybe I am just fluking it by not seeing 'proper' multi-threaded behaviour.

Any advice or pointers to good articles on this would be very much appreciated.

Kind wishes - Patrick

using System;
using System.Text;
using System.Threading;
 
namespace ThreadingTest
{
  class Program
  {
    static void Main()
    {
      Thread thread1 = new Thread( new ThreadStart( ProcessEntry ) );
      Thread thread2 = new Thread( new ThreadStart( ProcessEntry ) );
      thread1.Name = "0";
      thread2.Name = "10";
      thread1.Start();
      thread2.Start();
      thread1.Join();
      thread2.Join();
      Console.ReadLine();
    }
 
    private static void ProcessEntry()
    {
      Console.WriteLine( "{0} constructing StringBuilder", Thread.CurrentThread.Name );
      StringBuilder sb = new StringBuilder( Thread.CurrentThread.Name + ":" );
      for( int i = 0; i < 10; ++i )
      {
        int offset = Int32.Parse( Thread.CurrentThread.Name );
        sb.AppendFormat( "{0} ", offset + i );
        Thread.Sleep( 100 );
        Console.WriteLine( sb.ToString() );
        Thread.Sleep( 500 );
      }
      Console.WriteLine();
    }
  }
}
Patrick Skelton replied to Robbe Morris on 05-Mar-13 04:21 AM
Thank you, Robbe.  I will have a good look at that and try to digest it later.

I am not entirely sure, yet, how I will use it, because my code sits in a library, and I have no control over the threads that are created and subsequently make calls into my library.

With reference to my specific example, I am aware that the access to the Console is problematic, as it is a shared resource, and there is no guarantee that calls to write to it are atomic, but this was just put in for illustration, and is not present in the production code.

I was more interested in the possible problems with the StringBuilder.  The fact that I create one in the function (i.e. it is not static), I would have thought would make it safe.  Doesn't each call to ThreadStart() cause a fresh instance to be created?

The other issue is, of course, the integer loop variable, but, since this is a value type, doesn't this simply go on the stack, and doesn't each call to the ProcessEntry() function have its own stack?

As I said, I thought I understood this stuff (I have quite a long background in C++ and have used multithreading extensively), but I suddenly find myself unsure how my C++ knowledge translates across to the C# world.

I'd really like an article (or even a book) that explains in depth what to watch out for with multithreading in C#.
 
Kind wishes - Patrick
Robbe Morris replied to Patrick Skelton on 05-Mar-13 08:43 AM
In your code sample, each thread would be running in isolation.  So, StringBuilder would not be an issue.  You'll want to use the thread pool (my sample) to let it manage the number of active threads running at any given time to ensure your app is stable.

My sample creates and instance of a class and isolates all of the objects and work inside of it (obviously not the Console.Write stuff).  You'll want to make sure your code does the same.  I've had no issues passing in references to objects created on the main thread for read only purposes since they are just a reference.  You can run into issues if you want to write to it on multiple threads.  If you need that sort of thing, you've got to start looking to the database as your thread safe data storage mechanism.