Friday, November 18, 2005

ThreadStaticAttribute

I was experimenting with ThreadStaticAttribute.  A way to make available a new copy of class (note: not instance) variables to each of the threads running.

Static methods usually do not store state in class level variables for it would be shared by all other static methods too.  Unless marked ‘volatile’ two threads executing two different static methods on the same class could step on each other while assigning / reading from the variable.  Also, they get to share the copy of the state.  Now you could store state from a static method into “ThreadStatic” variables that only the same executing thread could see.  There’s a side effect though: If you happen to use a ThreadPool.  ThreadPool threads could be reused leading to state being visible when it isn’t supposed to be.  Well, you could always re-initialize before exiting.   The following example demonstrates that.

using System;
using System.Threading;

public class TestThreadStatic
{

     int completed = 0;
     public static void Main(string [] args)
     {
          TestThreadStatic tts = new TestThreadStatic();
          tts.StartTesting();
          while(tts.completed < 5)
          {
               Thread.Sleep(1000);
          }
     }


     public void StartTesting()
     {
          Console.WriteLine("Initial value of ticks at " +
" the beginning: {0}", ticks);
          for(int i = 0; i < 5; i++)
          {
               Console.WriteLine("Assigning work item {0}", i);
               ThreadPool.QueueUserWorkItem(ExecutedOnAThread, i);
          }

     }

     [ThreadStatic]
     private static long ticks;

     public void ExecutedOnAThread(object state)
     {
          string hashCode = Thread.CurrentThread.GetHashCode().ToString();
          //Initialize only once
          switch(ticks)
          {
               case 0:
                    Console.WriteLine("[{0}] A New Thread", hashCode);
                    ticks = -2;
                    break;
               case -1:
                    Console.WriteLine("[{0}] Thread reused", hashCode);
                    break;
               default:  // if ticks is -2
                    Console.WriteLine("[{0}] A supposedly new " +
                                   "thread is able to see "         +
"the old value {1}",
hashCode,
ticks);
                    break;
          }

          Console.WriteLine("[{0}] Ticks at starting: {1}"
, hashCode, ticks);
          ticks = DateTime.Now.Ticks;
          Console.WriteLine("[{0}] Ticks at End: {1}", hashCode, ticks);
          ticks = -1;

          //If sleep is reduced some of the threads would be reused
          Thread.Sleep(5000);
            completed++;
     }
}


Remember, the variable must be declared static.


Results (if running with a 5 second delay):


Initial value of ticks at the beginning: 0
Assigning work item 0
Assigning work item 1
Assigning work item 2
Assigning work item 3
Assigning work item 4
[3] A New Thread
[3] Ticks at starting: -2
[3] Ticks at End: 632691576162343750
[4] A New Thread
[4] Ticks at starting: -2
[4] Ticks at End: 632691576171406250
[5] A New Thread
[5] Ticks at starting: -2
[5] Ticks at End: 632691576176406250
[6] A New Thread
[6] Ticks at starting: -2
[6] Ticks at End: 632691576181406250
[7] A New Thread
[7] Ticks at starting: -2
[7] Ticks at End: 632691576186406250

If the delay is made 1 second:

Initial value of ticks at the beginning: 0
Assigning work item 0
Assigning work item 1
Assigning work item 2
Assigning work item 3
Assigning work item 4
[3] A New Thread
[3] Ticks at starting: -2
[3] Ticks at End: 632691595203906250
[3] Thread reused
[3] Ticks at starting: -1
[3] Ticks at End: 632691595213906250
[4] A New Thread
[4] Ticks at starting: -2
[4] Ticks at End: 632691595213906250
[5] A New Thread
[5] Ticks at starting: -2
[5] Ticks at End: 632691595218906250
[3] Thread reused
[3] Ticks at starting: -1
[3] Ticks at End: 632691595223906250

No comments: