evictednoise
evictednoise

Reputation: 593

Using Task.Factory.StartNew in a loop

I am trying to get a grasp on multi-thread programming in .NET.

I want to create 100 tasks in a for-loop, each task would sleep 1 sec then output the i it was created with and it's managed thread id. After all tasks finish I want to output the total time elapsed.

So I am running this simple code

    private static void Main(string[] args)
    {
        List<Task> tasks = new List<Task>();

        for (int i = 0; i < 100; i++)
        {
            tasks.Add(Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000);
                Console.WriteLine(i.ToString() + "  " + Thread.CurrentThread.ManagedThreadId);
            }));
        }
        Stopwatch watch = new Stopwatch();

        watch.Start();
        var final = Task.Factory.ContinueWhenAll(tasks.ToArray(), doneTasks =>
        {
            watch.Stop();
            Console.WriteLine("done in {0}", watch.Elapsed);
        });
        Console.ReadKey();
    }

The output I get:

100  24
100  23
...
100  14
100  25
done in 00:00:12.0044853

I don't understand why all the i's are same, and why 100? Intuitively I would expect them to be 0~99, but not all 100's.

Upvotes: 2

Views: 3413

Answers (2)

Yeldar Kurmangaliyev
Yeldar Kurmangaliyev

Reputation: 34189

Imagine the order of execution. You instruct every task to output i after a second, but by that time the loop has finished execution and i is equal to 100.

You can use the following StartNew overload to pass a state object which will be used by your action:

public Task StartNew(Action<object> action, object state)

And do the following:

for (int i = 0; i < 100; i++)
{
    tasks.Add(Task.Factory.StartNew(o =>
    {
        int value = (int)o;

        Thread.Sleep(1000);
        Console.WriteLine(value.ToString() + "  " + Thread.CurrentThread.ManagedThreadId);
    }, i));
}

Upvotes: 10

ESG
ESG

Reputation: 9425

That is because your tasks are being created before your closure gets executed. If you copy the value of I to a variable before the sleep call, or better yet, pass it as a parameter to your action, you should get the desired result.

Upvotes: 3

Related Questions