locked
Strange behavior with Task.run RRS feed

  • Question

  • public static void Main()
    {
        Console.WriteLine("Starting.");
    
        for (int i = 0; i < 4; ++i)
            Task.Run(() => Console.WriteLine(i));
    
        Console.WriteLine("Finished. Press <ENTER> to exit.");
        Console.ReadLine();
    }

    Can anyone check if it's bug in .net?

    Sunday, November 26, 2017 2:54 PM

Answers

  • Hello Animee.Young,

    >>Strange behavior with Task.run

    The issue is no relating to task.run method. the real course of the phenomenon is "closures".

    There is a very professional people given a understanding of "closures".

    Quote:In essence, a closure is a block of code which can be executed at a later time, but which maintains the environment in which it was first created - i.e. it can still use the local variables etc of the method which created it, even after that method has finished executing.

    And the mistake usually occurs in anonymous methods and lambda expressions. In your case, Because () => Console.WriteLine(k) means "it will write the current value of variable v in console". And when the methods run, clearly the last value that was assigned to k was 4,so it still has that value.

    The following is a simple demo for you understanding.

       Console.WriteLine("Starting.");
    
                var funcs = new List<Action>();
    
                for (int i = 0; i < 4; ++i)
                    funcs.Add(() => Console.WriteLine(i));
    
                for (int j = 0; j < funcs.Count; j++) {
                    funcs[j]();
                }
                Console.WriteLine("Finished. Press <ENTER> to exit.");
                Console.ReadLine();
    

    There is a MSDN blog that you could look up it.

    https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/

    Sincerely,

    Fei Hu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by Aimee.Young Monday, December 4, 2017 2:58 PM
    Monday, November 27, 2017 8:09 AM
  • Try this:

       for( int i = 0; i < 4; ++i )

       {

          var k = i;

          Task.Run( () => Console.WriteLine( k ) );

       }

     

    There is a single variable i, which is shared between tasks.

    • Proposed as answer by madmir Sunday, November 26, 2017 4:35 PM
    • Marked as answer by Aimee.Young Monday, December 4, 2017 2:58 PM
    Sunday, November 26, 2017 4:34 PM
  • Can anyone check if it's bug in .net?
    No, it is not a bug. It is a very common mistake that programmers make in interpreting how the closures work. What is happening is that the loop runs and enqueues 4 tasks. Then the four tasks start and they read the value of i, which by that time is 4. A simple solution is the one that was provided in a previous response: The "i" is copied into another variable, which will be a different variable on each iteration of the loop, and then that distinct variable, which has a different value of i, is the one that gets captured by the closure, so that each run of the task sees a different value.
    Sunday, November 26, 2017 7:03 PM

All replies

  • Try this:

       for( int i = 0; i < 4; ++i )

       {

          var k = i;

          Task.Run( () => Console.WriteLine( k ) );

       }

     

    There is a single variable i, which is shared between tasks.

    • Proposed as answer by madmir Sunday, November 26, 2017 4:35 PM
    • Marked as answer by Aimee.Young Monday, December 4, 2017 2:58 PM
    Sunday, November 26, 2017 4:34 PM
  • Can anyone check if it's bug in .net?
    No, it is not a bug. It is a very common mistake that programmers make in interpreting how the closures work. What is happening is that the loop runs and enqueues 4 tasks. Then the four tasks start and they read the value of i, which by that time is 4. A simple solution is the one that was provided in a previous response: The "i" is copied into another variable, which will be a different variable on each iteration of the loop, and then that distinct variable, which has a different value of i, is the one that gets captured by the closure, so that each run of the task sees a different value.
    Sunday, November 26, 2017 7:03 PM
  • Hello Animee.Young,

    >>Strange behavior with Task.run

    The issue is no relating to task.run method. the real course of the phenomenon is "closures".

    There is a very professional people given a understanding of "closures".

    Quote:In essence, a closure is a block of code which can be executed at a later time, but which maintains the environment in which it was first created - i.e. it can still use the local variables etc of the method which created it, even after that method has finished executing.

    And the mistake usually occurs in anonymous methods and lambda expressions. In your case, Because () => Console.WriteLine(k) means "it will write the current value of variable v in console". And when the methods run, clearly the last value that was assigned to k was 4,so it still has that value.

    The following is a simple demo for you understanding.

       Console.WriteLine("Starting.");
    
                var funcs = new List<Action>();
    
                for (int i = 0; i < 4; ++i)
                    funcs.Add(() => Console.WriteLine(i));
    
                for (int j = 0; j < funcs.Count; j++) {
                    funcs[j]();
                }
                Console.WriteLine("Finished. Press <ENTER> to exit.");
                Console.ReadLine();
    

    There is a MSDN blog that you could look up it.

    https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/

    Sincerely,

    Fei Hu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by Aimee.Young Monday, December 4, 2017 2:58 PM
    Monday, November 27, 2017 8:09 AM