0

Got a strange bug.

The output of following is:

RANGE: 100000 - 125000
RANGE: 100000 - 125000
RANGE: 100000 - 125000
RANGE: 100000 - 125000

And this is wrong!
If I add an extra local var in the loop, the output is right!

int ax = i;
var curtask = Task.Run(() =>Work(ax, blocksize, size));

Output:

RANGE: 75000 - 100000
RANGE: 0 - 25000
RANGE: 25000 - 50000
RANGE: 50000 - 75000

(it's ok that the order isn't kept)

So the i var is not given to the task on creation?
I find this situation very dangerous. Normally I would have trust in the parallels!

private static void Work(int startIndex, int blocksize, int maxSize)
{
    int end = startIndex + blocksize;
    Trace.WriteLine($"RANGE: {startIndex} - {end}");
    Console.WriteLine($"RANGE: {startIndex} - {end}");
}

static void Main(string[] args)
{
    int i = 0;
    int size = 100000;
    int blocksize = size/4;
    int taskcount = 0;
    var tasks = new List<Task>();

    while (i < size)
    {
        //THIS WORKS!!!
        //int ax = i;
        //var curtask = Task.Run(() =>Work(ax, blocksize, size));

        var curtask = Task.Run(() => Work(i, blocksize, size));
        taskcount++;
        tasks.Add(curtask);

        i = i + blocksize;
    }
    Task.WaitAll(tasks.ToArray());
    Console.WriteLine("DONE");
    Console.ReadKey();
}
GSerg
  • 76,472
  • 17
  • 159
  • 346
Crimson
  • 103
  • 6
  • 2
    You've made the common error of closing a lambda over a variable. This behaviour is unexpected to most people, but is correct. See https://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/ – Eric Lippert Nov 26 '19 at 08:42
  • 3
    If you do a search here for "C# lambda closed over loop variable" or similar, you will find a great many similar questions. – Eric Lippert Nov 26 '19 at 08:44

3 Answers3

1

All seems right to me. i is captured. You have only one i which is shared by all tasks, but you have many ax's and each of them has different value.

Maybe you can read about closures, for example here: Are Lambda expressions in C# closures?

Al Kepp
  • 5,831
  • 2
  • 28
  • 48
0

This happens because on Task.Run call the created task doesn't run immediately but is created only (WaitingToRun status proves this)

enter image description here

So for example the first created task hasn't started yet but another iteration already happened and i is incremented and so on.

Alexander
  • 9,104
  • 1
  • 17
  • 41
-2

Variables that are passed to a task are accessed something like by reference (c# keeps the value of variable updated). So, if you send the variable i to each task, its value will be updated for all of your tasks as the loop progresses. You must create a local variable before sending i to send a copy of i at the current moment to each task. What you are commented in your source code is a correct way to do this.
Here, your all tasks are just started after the ending of the while loop. So, they see the last value of the i.