3

I'm creating System.Threading.Tasks from within a for loop, then running a ContinueWith within the same loop.

int[,] referencedArray = new int[input.Length / 4, 5];
   for (int i = 0; i <= input.Length/4; i += 2048)
     {
       int[,] res = new int[input.Length / 4, 5];
       int[,] arrayToReference = new int[input.Length / 4, 5];
       Array.Clear(arrayToReference, 0, arrayToReference.Length);
       Array.Copy(input, i, arrayToReference, 0, input.Length / 4);
       Task<Tuple<int[,], int>> test = Task.Factory.StartNew(() => addReferences(arrayToReference, i));
        test.ContinueWith(t => { Array.Copy(t.Result.Item1, 0, referencedArray, t.Result.Item2, input.Length / 4); MessageBox.Show("yai", t.Result.Item2.ToString()); });
        Array.Copy(res, 0, referencedArray, i, input.Length / 4);

where the addReference sbroutine is:

public Tuple<int[,],int> addReferences(int[,] input, int index)
    {
            for (int i = 0; i < 2048; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    if ((input[i, 0] == input[j, 0]) && (input[i, 1] == input[j, 1]) && (input[i, 2] == input[j, 2]) && (input[i, 3] == input[j, 3]))
                    {
                        input[i, 4] = (j - i);
                    }
                }
            }
        }
        return new Tuple<int[,],int>(input,index);
    }

However, I am getting really strange results:

1.The index (generated from the loop counter when the task is started) somehow becomes too large. I initially thought that this was because the loop counter had incremented past its maximum, stopping the loop, but when the continueWith executed it used the new value. However, even when I send the loop counter's value to the task as index when it starts, the error persists. Edit: Solved

I made I mistake in the original count

2.For some reason fewer than expected tasks are actually completed (e.g. even though 85 tasks are expected to be created, based on a loop counter maximum of 160,000 divided by a iteration of 2,048 = 78, only 77 pop ups appeared) - Edit Checking the console, it's apparent that the loop counter got up until about 157,696 before suddenly stopping.

I'm really confused by Tasks and Threading, so if you could be of any assistance that would be really useful - thanks in advance.

Edit I've made the changes suggested by Douglas, which solved my first problem - I'm still stuck on the second.

anton.burger
  • 5,637
  • 32
  • 48
FireOak
  • 383
  • 3
  • 16
  • Have you tried just doing a console output to make sure there is *definitely* an issue with the threads not being completed? Personally I think the idea of 70 odd popups in an app as hell... – James Jul 05 '13 at 11:36
  • I tried the console output, and again it only got up until 157,000 before stopping – FireOak Jul 05 '13 at 11:43

1 Answers1

4

This might not completely resolve the issues you're having, but as a starting point, you need to avoid the closure issue by copying your loop counter to an inner variable before referencing it in your anonymous method:

for (int i = 0; i <= input.Length/4; i += 2048)
{
    // ...
    int iCopy = i;
    var test = Task.Factory.StartNew(() => addReferences(arrayToReference, iCopy));
    // ...
}

See Jon Skeet's answer (particularly the linked article) for an explanation of why this is necessary.

Edit: Regarding your second problem, I suspect it might have to do with integer division. What is the value of input.Length for which you're expecting 85 iterations? You say that 174,000 divided by 2,048 should give 85, but in practice, it will give 84, since integer division causes results to be truncated.

Edit2: Another reason you're not seeing all expected iterations could be that your program is terminating without waiting for the tasks (which typically execute on background threads) to complete. Check whether waiting on the tasks resolves your issue:

List<Task> tasks = new List<Task>();
for (int i = 0; i <= input.Length/4; i += 2048)
{
    // ...
    var test = Task.Factory.StartNew(() => addReferences(arrayToReference, iCopy));
    tasks.Add(test);
    // ...
}
Task.WaitAll(tasks);
Community
  • 1
  • 1
Douglas
  • 53,759
  • 13
  • 140
  • 188
  • Thank you, that solved the first problem - no luck on the second I'm afraid – FireOak Jul 05 '13 at 11:31
  • About the second bit - I'm not sure it's a division error, since it got up to 157,000 and suddenly stopped - it only completed 79, which seems too low – FireOak Jul 05 '13 at 11:45
  • Yes, but what is `input.Length`? – Douglas Jul 05 '13 at 11:48
  • `Input.Length` is the length of a 4 dimensional array that I send off in chunks to `Tasks` for processing, and is then returned in the `ContinueWith` and copied into the main array. – FireOak Jul 05 '13 at 11:52
  • I mean, what is its value in your current test (for which you expect to get 85 iterations)? – Douglas Jul 05 '13 at 11:53
  • sorry - for the test case I'm currently using it's 6,400,000 – FireOak Jul 05 '13 at 11:55
  • Are you sure? If that's the case, shouldn't it give 781 iterations, rather than 85? – Douglas Jul 05 '13 at 11:58
  • Wow, sorry - It's actually 160,000 (don't know what went through my head) - like you said, there should actually only be 78 - the problem is I'm only getting 77 right now – FireOak Jul 05 '13 at 12:09
  • I assume that 160,000 is what you (expect to) get after dividing `input.Length` by 4. What is the value of `input.Length`? I suspect that you're still rounding up your division instead of truncating it. – Douglas Jul 05 '13 at 12:11
  • Sorry - `input.length` is 640,000 - It's really late where I am :) – FireOak Jul 05 '13 at 12:13
  • That should actually give 79 iterations. Try using the `Task.WaitAll` solution I pasted above. – Douglas Jul 05 '13 at 12:16