0

I'm new to the Parallel class in .NET. I have this basic code:

        List<Action> actions = new List<Action>();

        for (int iter = 1; iter <= 5; iter++)
        {
            actions.Add(() =>
            {
                // create a local copy of iter, and an action to write it to the console
                int theProblem = iter;
                Console.WriteLine(theProblem);
            });
        }

        Parallel.Invoke(actions.ToArray());

        /*
        Output:
        6
        6
        6
        6
        6
        */

I would expect this to print 1..5 in some random order, but instead it prints out the value of iter after the loop ends.

Why is this? At what point are the actions actually created, and the line int theProblem = iter; executed?

I found a TON of information online about Parallel.For and Parallel.ForEach (not so much for Parallel.Invoke), but nothing explaining specifically what goes on in a very basic case like this one.

What modifications do I need to make to have the code above print 1..5 (in some random order)?

Thanks a lot!

EDIT:

This question has been marked as a duplicate, but I don't think it is:

  • All except one of the responses in the other question refer to sigle-threaded code, this is multi-threaded.

  • None of the responses refers to Parallel.Invoke (or to any other method in the Parallel class for that matter).

  • My code sample already implements the solutions proposed in the other question (create a copy of the variable inside the loop), and yet it's not working.

So, clearly not a dupe.

EDIT 2:

Turns out that this is actually a dupe under the hood. The gist of it is to create the local copy of the variable inside the loop but outside the lambda (duh!) and then everything works just as expected.

Thanks everyone!

d..
  • 1,063
  • 1
  • 10
  • 18
  • It has nothing to do with "when the delegates are created". The problem observed is that `iter` is the *same variable* bound in all the closures. It is "not fresh". To fix this, eg: `for (int iter = 1; iter <= 5; iter++) { int freshIter = iter; /* then use freshIter in closure */ }`. – user2864740 Aug 27 '17 at 19:52
  • Thanks! But is that not what I'm doing in `int theProblem = iter;`? Can you clarify? – d.. Aug 27 '17 at 19:58
  • In the code the assignment happens *when* the callback is executed (everything inside the `() => ..` runs *on* execution, the creation of the callback is what happens immediately and the resulting Action-object is what is stored in the List; creating the Action does *not* invoke any contained code). The Actions are executed *after* the loop has completed running: at this point `iter` already has the final value (6) assigned. – user2864740 Aug 27 '17 at 20:02
  • Got it! Is there an easy way to store in the Action the value in the variable when the Action was created? In other words, what are the minimum changes I need to make in my code to have it print 1..5 in some order? Thanks! – d.. Aug 27 '17 at 20:14
  • @gonzalofg Put `int theProblem = iter;` outside of lambda. – user4003407 Aug 27 '17 at 21:54
  • @gonzalofg Creating a "fresh" variable inside the loop is sufficient (see above and first comment) in this case. The assignment to this variable happens during the respective iteration (with the value for the loop) and it is this *new* variable that is captured in the closure formed by the Action. – user2864740 Aug 27 '17 at 22:19
  • Oh I see; *now* I got it! I'll give it a try as soon as I can; thank you both! – d.. Aug 27 '17 at 22:33
  • 1
    "So, clearly not a dupe." I don't see that that's clear at all. The problem has nothing to do with the code running in parallel, and everything to do with capturing `iter`. You're creating a load of delegates which all refer to the same variable, then executing them in parallel... the execution in parallel really doesn't affect this at all. – Jon Skeet Aug 28 '17 at 07:44
  • 1
    (And no, your code sample doesn't implement the solution proposed in the other question, because your assignment to `theProblem` is within the lambda expression.) – Jon Skeet Aug 28 '17 at 07:45
  • You're correct. My moronic self only realized what he was doing wrong after reading @PetSerAl comment. Just edited the question to reflect this. Thanks a lot! – d.. Aug 29 '17 at 00:58
  • By "you" in my previous comment I meant @JonSkeet – d.. Aug 29 '17 at 01:07
  • So, just to close this: if one of you guys posts the answer, I'll accept it. – d.. Sep 02 '17 at 19:41

0 Answers0