Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why task factory in loop prints beyond loop index?

I am learning to use the Task Parallel Library(TPL) in C#, and wrote the following code (you can copy-past it and run it).

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

namespace parallelTaskLibrary
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 6; i++)
            {
                var t01 = Task.Factory.StartNew(() => Console.WriteLine("in loop: i = {0}", i));
            }

            Console.WriteLine("press any key to terminate...");
            Console.ReadKey();
        }      
    }
}

in the for loop, the counter index i cannot start an iteration with a value of i = 6. However, the output i got is this:

press any key to terminate...
in loop: i = 6
in loop: i = 6
in loop: i = 6
in loop: i = 6
in loop: i = 6
in loop: i = 6

However, in another launch(didn't change in the code), i got this:

in loop: i = 1
in loop: i = 1
in loop: i = 2
in loop: i = 3
in loop: i = 4
in loop: i = 5
press any key to terminate...

which is plausible...

I Debugged the code, and found that i value is: 0,1,3,4,5,6

How did that happen?

Why i got (i = 6) in the loop?

is there anything wrong in my code?

Note: I'm using visual studio 2010

like image 541
ThunderWiring Avatar asked Nov 30 '22 16:11

ThunderWiring


2 Answers

because there is a closure problem. you need to copy the variable i in temporary variable.

for (int i = 0; i < 6; i++)
{
    var tempi = i;
    var t01 = Task.Factory.StartNew(() => Console.WriteLine("in loop: i = {0}", tempi));
}

Because your task starts in another thread. but you dont wait for that task in current thread. so the program will comeback and will increment the counter. since the delegate you have created uses the original counter then it prints 6. because counting to 6 is done much faster than creating a new task.

When you use debugger the new task will get a chance to print the value before you continue to increment the counter,

like image 153
M.kazem Akhgary Avatar answered Dec 06 '22 18:12

M.kazem Akhgary


Obviously, you will print the value of "i" as it is when your code (the delegate with the Console.WriteLine statement) is executed, which may very well be at a slightly later time.

This is probaby what you want:

for (int i = 0; i < 6; i++)
{
  var j = i;
  var t01 = Task.Factory.StartNew(() => Console.WriteLine("in loop: i = {0}", j));
}

>is there anything wrong in my code?

Yes :-)

like image 27
Dan Byström Avatar answered Dec 06 '22 18:12

Dan Byström