views:

1075

answers:

8

I've found the "ThreadStatic" attribute to be extremely useful recently, but makes me now want a "ThreadLocal" type attribute that lets me have non-static data members on a per-thread basis.

Now I'm aware that this would have some non-trivial implications, but:

Does such a thing exist already built into C#/.net? or since it appears so far that the answer to this is no (for .net < 4.0), is there a commonly used implementation out there?

I can think of a reasonable way to implement it myself, but would just use something that already existed if it were available.

Straw Man example that would implement what I'm looking for if it doesn't already exist:

class Foo
{
    [ThreadStatic] 
    static Dictionary<Object,int> threadLocalValues = new Dictionary<Object,int>();
    int defaultValue = 0;

    int ThreadLocalMember
    {
         get 
         { 
              int value = defaultValue;
              if( ! threadLocalValues.TryGetValue(this, out value) )
              {
                 threadLocalValues[this] = value;
              }
              return value; 
         }
         set { threadLocalValues[this] = value; }
    }
}

Please forgive any C# ignorance. I'm a C++ developer that has only recently been getting into the more interesting features of C# and .net

I'm limited to .net 3.0 and maybe 3.5 (project has/will soon move to 3.5).

Specific use-case is callback lists that are thread specific (using imaginary [ThreadLocal] attribute) a la:

class NonSingletonSharedThing
{
     [ThreadLocal] List<Callback> callbacks;

     public void ThreadLocalRegisterCallback( Callback somecallback )
     {    
         callbacks.Add(somecallback);    
     }

     public void ThreadLocalDoCallbacks();
     {    
         foreach( var callback in callbacks )  
            callback.invoke();  
     }
}
+4  A: 

Enter .NET 4.0!

If you're stuck in 3.5 (or earlier), there are some functions you should look at, like AllocateDataSlot which should do what you want.

Travis Gockel
Definitely still in 3.0 or 3.5; 4.0 is not an option for me at the moment. Otherwise that 4.0 stuff looks like it might be what I'm looking for. Sad thing is that it probably means nothing like this exists in .net < 4.0 : /
Catskul
You could always use Reflector to reverse-engineer the ThreadLocal<T> implementation from .NET4, include it in your project and when you eventually move to .NET4 just remove your reverse-engineered implementation (everything else will remain the same). I used this technique successfully with TimeZoneInfo when moving from .NET2 to .NET 3.5.
Milan Gardian
@Milan: This looks like the most tenable solution as it's looking pretty clear that what I'm looking for does not exist in .net 3.x
Catskul
+2  A: 

If you looking to store unique data on a per thread basis you could use Thread.SetData. Be sure to read up on the pros and cons http://msdn.microsoft.com/en-us/library/6sby1byh.aspx as this has performance implications.

iaimtomisbehave
Thanks. That's interesting but more than a little bit awkward. I'd probably use my straw man example before I'd use that unfortunately.
Catskul
+4  A: 

You should think about this twice. You are essentially creating a memory leak. Every object created by the thread stays referenced and can't be garbage collected. Until the thread ends.

Hans Passant
Good point, though it could be figured out/taken care of. All the more reason to use built in functionality if it exists as it would take care of this.
Catskul
A: 

I'm not sure how you're spawning your threads in the first place, but there are ways to give each thread its own thread-local storage, without using hackish workarounds like the code you posted in your question.

public void SpawnSomeThreads(int threads)
{
    for (int i = 0; i < threads; i++)
    {
        Thread t = new Thread(WorkerThread);

        WorkerThreadContext context = new WorkerThreadContext
        {
            // whatever data the thread needs passed into it
        };

        t.Start(context);
    }
}

private class WorkerThreadContext
{
    public string Data { get; set; }
    public int OtherData { get; set; }
}

private void WorkerThread(object parameter)
{
    WorkerThreadContext context = (WorkerThreadContext) parameter;

    // do work here
}

This obviously ignores waiting on the threads to finish their work, making sure accesses to any shared state is thread-safe across all the worker threads, but you get the idea.

Zack Elan
I'm not worried about giving each thread it's own thread local storage. Thread local storage is readily provided by c#. I'm interesting in giving objects thread local members.
Catskul
+1  A: 

Consider:

Rather than try to give each member variable in an object a thread-specific value, give each thread its own object instance. -- pass the object to the threadstart as state, or make the threadstart method a member of the object that the thread will "own", and create a new instance for each thread that you spawn.

Edit (in response to Catskul's remark. Here's an example of encapsulating the struct


public class TheStructWorkerClass
{
  private StructData TheStruct;
  public TheStructWorkerClass(StructData yourStruct)
  {
    this.TheStruct = yourStruct;
  }

  public void ExecuteAsync()
  {
    System.Threading.ThreadPool.QueueUserWorkItem(this.TheWorkerMethod);
  }
  private void TheWorkerMethod(object state)
  {
     // your processing logic here
     // you can access your structure as this.TheStruct;
     // only this thread has access to the struct (as long as you don't pass the struct
     // to another worker class)
  }
}

// now hte code that launches the async process does this:
  var worker = new TheStructWorkerClass(yourStruct);
  worker.ExecuteAsync();

Now here's option 2 (pass the struct as state)


 {
 // (from somewhere in your existing code
    System.Threading.Threadpool.QueueUserWorkItem(this.TheWorker, myStruct);
 } 

  private void TheWorker(object state)
  { 
    StructData yourStruct = (StructData)state;
    // now do stuff with your struct
    // works fine as long as you never pass the same instance of your struct to 2 different threads.
  }

JMarsch
+1 This is the right approach.
Steven Sudit
I do not own the structure surrounding this so it is not an option to restructure it as you are suggesting. Even if I did, for my specific situation restructuring it in that way would not be appropriate.
Catskul
@Catskul: If you can't restructure that struct, you can either encapsulated it, or pass it to the thread as state. If you pass it as state, then you need to create 1 struct per thread (but you don't have to change anything about the struct). To encapsulate, you would define a class that owns both your struct and contains the threadstart. The thread would run for instances of that class and access that class's private copy of the struct. I'll edit my response with an example.
JMarsch
I didnt actually mean Structure as in struct, I meant the larger system that I'm working in is part of a larger project. I'm not at liberty to restructure that. Knowing when and where other threads are being created and placing upkeep or constraints on it is not something this can/should be doing.
Catskul
+1  A: 

Although I am still not sure about when your use case would make sense (see my comment on the question itself), I would like to contribute a working example that is in my opinion more readable than thread-local storage (whether static or instance). The example is using .NET 3.5:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Linq;

namespace SimulatedThreadLocal
{
    public sealed class Notifier
    {
        public void Register(Func<string> callback)
        {
            var id = Thread.CurrentThread.ManagedThreadId;
            lock (this._callbacks)
            {
                List<Func<string>> list;
                if (!this._callbacks.TryGetValue(id, out list))
                {
                    this._callbacks[id] = list = new List<Func<string>>();
                }
                list.Add(callback);
            }
        }

        public void Execute()
        {
            var id = Thread.CurrentThread.ManagedThreadId;
            IEnumerable<Func<string>> threadCallbacks;
            string status;
            lock (this._callbacks)
            {
                status = string.Format("Notifier has callbacks from {0} threads, total {1} callbacks{2}Executing on thread {3}",
                    this._callbacks.Count,
                    this._callbacks.SelectMany(d => d.Value).Count(),
                    Environment.NewLine,
                    Thread.CurrentThread.ManagedThreadId);
                threadCallbacks = this._callbacks[id]; // we can use the original collection, as only this thread can add to it and we're not going to be adding right now
            }

            var b = new StringBuilder();
            foreach (var callback in threadCallbacks)
            {
                b.AppendLine(callback());
            }

            Console.ForegroundColor = ConsoleColor.DarkYellow;
            Console.WriteLine(status);
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(b.ToString());
        }

        private readonly Dictionary<int, List<Func<string>>> _callbacks = new Dictionary<int, List<Func<string>>>();
    }

    public static class Program
    {
        public static void Main(string[] args)
        {
            try
            {
                var notifier = new Notifier();
                var syncMainThread = new ManualResetEvent(false);
                var syncWorkerThread = new ManualResetEvent(false);

                ThreadPool.QueueUserWorkItem(delegate // will create closure to see notifier and sync* events
                {
                    notifier.Register(() => string.Format("Worker thread callback A (thread ID = {0})", Thread.CurrentThread.ManagedThreadId));
                    syncMainThread.Set();
                    syncWorkerThread.WaitOne(); // wait for main thread to execute notifications in its context

                    syncWorkerThread.Reset();
                    notifier.Execute();
                    notifier.Register(() => string.Format("Worker thread callback B (thread ID = {0})", Thread.CurrentThread.ManagedThreadId));
                    syncMainThread.Set();
                    syncWorkerThread.WaitOne(); // wait for main thread to execute notifications in its context

                    syncWorkerThread.Reset();
                    notifier.Execute();
                    syncMainThread.Set();
                });

                notifier.Register(() => string.Format("Main thread callback A (thread ID = {0})", Thread.CurrentThread.ManagedThreadId));
                syncMainThread.WaitOne(); // wait for worker thread to add its notification

                syncMainThread.Reset();
                notifier.Execute();
                syncWorkerThread.Set();
                syncMainThread.WaitOne(); // wait for worker thread to execute notifications in its context

                syncMainThread.Reset();
                notifier.Register(() => string.Format("Main thread callback B (thread ID = {0})", Thread.CurrentThread.ManagedThreadId));
                notifier.Execute();
                syncWorkerThread.Set();
                syncMainThread.WaitOne(); // wait for worker thread to execute notifications in its context

                syncMainThread.Reset();
            }
            finally
            {
                Console.ResetColor();
            }
        }
    }
}

When you compile and run the above program, you should get output like this: alt text

Based on your use-case I assume this is what you're trying to achieve. The example first adds two callbacks from two different contexts, main and worker threads. Then the example runs notification first from main and then from worker threads. The callbacks that are executed are effectively filtered by current thread ID. Just to show things are working as expected, the example adds two more callbacks (for a total of 4) and again runs the notification from the context of main and worker threads.

Note that Notifier class is a regular instance that can have state, multiple instances, etc (again, as per your question's use-case). No static or thread-static or thread-local is used by the example.

I would appreciate if you could look at the code and let me know if I misunderstood what you're trying to achieve or if a technique like this would meet your needs.

Milan Gardian
Thank you for the effort you put into this answer. However without getting into a large amount of details, this is something that would not help me. I'm really only interested in whether per-thread non-static data members were available in .net 3.x or not (in the way that per thread statics are).
Catskul
A: 

I ended up implementing and testing a version of what I had originally suggested:

public class ThreadLocal<T>
{
    [ThreadStatic] private static Dictionary<object, T> _lookupTable;

    private Dictionary<object, T> LookupTable
    {
        get
        {
            if ( _lookupTable == null)
                _lookupTable = new Dictionary<object, T>();

            return _lookupTable;
        }
    }


    private object key = new object(); //lazy hash key creation handles replacement
    private T originalValue;

    public ThreadLocal( T value )
    {
        originalValue = value;
    }

    ~ThreadLocal()
    {
        LookupTable.Remove(key);
    }

    public void Set( T value)
    {
        LookupTable[key] = value;
    }

    public T Get()
    {
        T returnValue = default(T);
        if (!LookupTable.TryGetValue(key, out returnValue))
            Set(originalValue);

        return returnValue;
    }
}
Catskul
A: 

Whilst the posted solution looks elegant, it leaks objects. The finalizer - LookupTable.Remove(key) - is run only in the context of the GC thread so is likely only creating more garbage in creating another lookup table.

You need to remove object from the lookup table of every thread that has accessed the ThreadLocal. The only elegant way I can think of solving this is via a weak keyed dictionary - a data structure which is strangely lacking from c#.

Mania
This would probably be better used as a comment to the specific answer you're referring to than as an answer itself. Am I correct in assuming that you are referring to my proposed implementation?
Catskul
Sorry I'm not yet able to add comments to answers (low rep). I am referring to your proposed implementation. You're using the finalizer to remove the LookupTable key which at a glance seems fine, however consider the situation where the GC runs in it's own thread. All that happens when it runs is it creates a new LookupTable, and attempts to remove the key from it, achieving nothing. The only situation where it doesn't leak objects is where the GC runs in the context of the only thread that ever accessed the ThreadLocal - an unlikely scenario.
Mania