Search code examples
c#task-parallel-librarydataflowtpl-dataflow

How to create object per thread in DataFlow Block but not object per request?


I have a sample of code

var options = new ExecutionDataflowBlockOptions();
var actionBlock = new ActionBlock<int>(async request=>{
         var rand = new Ranodm();
         //do some stuff with request by using rand
},options);

And the problem with this code is that in every request I have to create new rand object. There is a way to define one rand object per thread and re-use the same object when handling requests?


Solution

  • Thanks to Reed Copsey for the nice article and Theodor Zoulias for reminding about ThreadLocal<T>.

    The new ThreadLocal class provides us with a strongly typed, locally scoped object we can use to setup data that is kept separate for each thread. This allows us to use data stored per thread, without having to introduce static variables into our types. Internally, the ThreadLocal instance will automatically setup the static data, manage its lifetime, do all of the casting to and from our specific type. This makes developing much simpler.

    A msdn example:

     // Demonstrates:
        //      ThreadLocal(T) constructor
        //      ThreadLocal(T).Value
        //      One usage of ThreadLocal(T)
        static void Main()
        {
            // Thread-Local variable that yields a name for a thread
            ThreadLocal<string> ThreadName = new ThreadLocal<string>(() =>
            {
                return "Thread" + Thread.CurrentThread.ManagedThreadId;
            });
    
            // Action that prints out ThreadName for the current thread
            Action action = () =>
            {
                // If ThreadName.IsValueCreated is true, it means that we are not the
                // first action to run on this thread.
                bool repeat = ThreadName.IsValueCreated;
    
                Console.WriteLine("ThreadName = {0} {1}", ThreadName.Value, repeat ? "(repeat)" : "");
            };
    
            // Launch eight of them.  On 4 cores or less, you should see some repeat ThreadNames
            Parallel.Invoke(action, action, action, action, action, action, action, action);
    
            // Dispose when you are done
            ThreadName.Dispose();
        }
    

    So your code would like this:

    ThreadLocal<Random> ThreadName = new ThreadLocal<Random>(() =>
    {
        return new Random();
    });
    
    
    var options = new ExecutionDataflowBlockOptions
    {
        MaxDegreeOfParallelism = 4,
        EnsureOrdered = false,
        BoundedCapacity = 4 * 8
    };
    var actionBlock = new ActionBlock<int>(async request =>
    {
        bool repeat = ThreadName.IsValueCreated;
        var random = ThreadName.Value; // your local random class
        Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId},  
            repeat: ", repeat ? "(repeat)" : "");
    
     }, options);