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.
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));
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With