title: concurrent access to static TryParse methods
body: <p>I recently wrote a flat-file data profiler in C# - partly as just an interesting project. The profiler uses the various C# <code>TryParse</code> methods to determine data types. </p>
  
  <p>Performance was poor and so I experimented with some different multi-threading approaches. (I had run the performance analyzer in Studio and it pointed to the parsing as a major chunk of the CPU time.) </p>
  
  <p>I didn't see much improvement from threading and so I poked around the .Net code I could find on the web (<code>referencesource.microsoft.com</code>). The various parse methods are all static and appear to function in non-thread-safe manner. </p>
  
  <p>Yet the threaded code produced the correct results. I didn't see any attributes that would have indicated thread synchronization in the .Net code. Which made me wonder if there was some synchronization mechanism that .Net was enforcing that I couldn't see in the code directly. </p>
  
  <p>Does anyone know if the .Net runtime might be synchronizing access to the static parse methods somehow? That would explain </p>
  
  <p>a) correct results with concurrent access to static methods and </p>
  
  <p>b) no real performance improvement because the statics are a bottleneck. </p>
  
  <p>I'm just puzzled.</p>
  
  <p>UPDATED 32-Nov -- Here is a stripped-down version to illustrate:</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Threading;
  using System.Threading.Tasks;
  
  namespace parse
  {
      class Program
      {
          static void Main(string[] args)
          {
              int totalParseCount = 50000000;
              int maxThreads = 8;
  
              Console.WriteLine("TryParse\n--------");
              for (int threads = 1; threads &lt;= maxThreads; ++threads)
              {
                  List&lt;Task&gt; tasks = new List&lt;Task&gt;();
                  DateTime start = DateTime.Now;
                  Console.WriteLine(string.Format("Thread count: {0}", threads));
                  for (int i = 0; i &lt; threads; ++i)
                  {
                      tasks.Add(Task.Run(() =&gt;
                      {
                          int threadParseCount = 0;
                          while (threadParseCount &lt; totalParseCount / threads)
                          {
                              int intOut;
                              long longOut;
                              DateTime dtOut;
                              decimal decOut;
                              int.TryParse("1b12i341i24b1234bo123", out intOut);
                              long.TryParse("1b12i341i24b1234bo123", out longOut);
                              DateTime.TryParse("1b12i341i24b1234bo123", out dtOut);
                              decimal.TryParse("1b12i341i24b1234bo123", out decOut);
                              ++threadParseCount;
                          }
                          Console.WriteLine(string.Format("  Thread: {0}; Parse ops: {1}", Thread.CurrentThread.ManagedThreadId, threadParseCount));
                      }));
                  }
                  Task.WaitAll(tasks.ToArray());
                  Console.WriteLine(string.Format("Elapsed (HH:MM:SS.Milli): {0}", DateTime.Now - start));
              }
          }
      }
  }
  </code></pre>
  
  <p>...and here is the output on my system (4 dual-core):</p>
  
  <pre>
  TryParse
  --------
  Thread count: 1
    Thread: 3; Parse ops: 50000000
  Elapsed (HH:MM:SS.Milli): 00:01:31.8389593
  Thread count: 2
    Thread: 4; Parse ops: 25000000
    Thread: 3; Parse ops: 25000000
  Elapsed (HH:MM:SS.Milli): 00:00:58.8212779
  Thread count: 3
    Thread: 3; Parse ops: 16666666
    Thread: 4; Parse ops: 16666666
    Thread: 6; Parse ops: 16666666
  Elapsed (HH:MM:SS.Milli): 00:00:47.8153264
  Thread count: 4
    Thread: 6; Parse ops: 12500000
    Thread: 5; Parse ops: 12500000
    Thread: 4; Parse ops: 12500000
    Thread: 3; Parse ops: 12500000
  Elapsed (HH:MM:SS.Milli): 00:00:42.6872096
  Thread count: 5
    Thread: 5; Parse ops: 10000000
    Thread: 6; Parse ops: 10000000
    Thread: 3; Parse ops: 10000000
    Thread: 7; Parse ops: 10000000
    Thread: 4; Parse ops: 10000000
  Elapsed (HH:MM:SS.Milli): 00:00:44.5965817
  Thread count: 6
    Thread: 5; Parse ops: 8333333
    Thread: 6; Parse ops: 8333333
    Thread: 4; Parse ops: 8333333
    Thread: 8; Parse ops: 8333333
    Thread: 3; Parse ops: 8333333
    Thread: 7; Parse ops: 8333333
  Elapsed (HH:MM:SS.Milli): 00:00:41.9454271
  Thread count: 7
    Thread: 9; Parse ops: 7142857
    Thread: 5; Parse ops: 7142857
    Thread: 6; Parse ops: 7142857
    Thread: 7; Parse ops: 7142857
    Thread: 4; Parse ops: 7142857
    Thread: 3; Parse ops: 7142857
    Thread: 8; Parse ops: 7142857
  Elapsed (HH:MM:SS.Milli): 00:00:40.8728885
  Thread count: 8
    Thread: 10; Parse ops: 6250000
    Thread: 5; Parse ops: 6250000
    Thread: 9; Parse ops: 6250000
    Thread: 7; Parse ops: 6250000
    Thread: 6; Parse ops: 6250000
    Thread: 4; Parse ops: 6250000
    Thread: 8; Parse ops: 6250000
    Thread: 3; Parse ops: 6250000
  Elapsed (HH:MM:SS.Milli): 00:00:37.8051313
  </pre>
  
  <p>There is an improvement but - certainly not close to linear with the increase in threads. I doubt there' a clear cut explanation - but I wanted to share the code since a couple folks took the time to respond. Thanks.</p>