Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why C# ThreadLocal does not work as expected with Task in TPL?

cannot understand an example in a book from apress talking about a misused case of threadlocal with task construct in TPL.

Why isn't it become 10000 in number as expected results?

Could anyone give a more detailed explanation on the program flow of below program for which line execute instantly and some lines async in time? the sequence and order of execution?

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Listing_05 {

class BankAccount {
    public int Balance {
        get;
        set;
    }
}

class Listing_05 {

    static void Main(string[] args) {

        // create the bank account instance
        BankAccount account = new BankAccount();

        // create an array of tasks
        Task<int>[] tasks = new Task<int>[10];

        // create the thread local storage
        ThreadLocal<int> tls = new ThreadLocal<int>(() => {
            Console.WriteLine("Value factory called for value: {0}",
               account.Balance);
            return account.Balance;
        });

        for (int i = 0; i < 10; i++) {
            // create a new task
            tasks[i] = new Task<int>(() => {

                // enter a loop for 1000 balance updates
                for (int j = 0; j < 1000; j++) {
                    // update the TLS balance
                    tls.Value++;
                }

                // return the updated balance
                return tls.Value;

            });

            // start the new task
            tasks[i].Start();
        }

        // get the result from each task and add it to
        // the balance
        for (int i = 0; i < 10; i++) {
            //added by myself to see any hints but still cannot have insights
            Console.WriteLine("task {0} results {1}", i, tasks[i].Result);
            //end of my editing

            account.Balance += tasks[i].Result;
        }

        // write out the counter value
        Console.WriteLine("Expected value {0}, Balance: {1}",
            10000, account.Balance);

        // wait for input before exiting
        Console.WriteLine("Press enter to finish");
        Console.ReadLine();
    }
}

}

Results in a computer using 8 cores i7 cpu, shall be 8 threads. Run Several times and below are 2 out of many executions.

1st execute

2nd execute

Do not understand how the programs work and behave in this way

like image 867
Cuda Avatar asked Oct 29 '25 13:10

Cuda


1 Answers

Tasks provide a mechanism to run code concurrently. This does not mean they will necessarily run on separate threads or even if multiple threads are used that they will not be reused.

Do not try to use thread local storage with Tasks at all.

Alternative storage options are to use a closure within the tasks's lambda function, or create a class and put data on that class and use a method of that class as the Task's callee. I personally feel this is a lot cleaner anyways.

Hans Passant also mentioned AsyncLocal in comments which would be worth researching (I haven't used it myself so can't comment).

like image 133
Samuel Neff Avatar answered Oct 31 '25 02:10

Samuel Neff