0

I was meet a strange bug, when I used the following code:

            for (int i = 0; i < 10; i++)
            {
                new Thread(() => Console.Write(i)).Start();
            }
            Console.WriteLine();
            for (int i = 0; i < 10; i++)
            {
                var temp = i;
                new Thread(() => Console.Write(temp)).Start();
            }

Results was unpredictable:

444554689

100123456789

I can`t understand why in the second cycle there is a problem with threads if I use a temporary variable. I think this is due to the same name for variable "i" , because when I change it, the problem was dissapeared. But they have different scopes.


So, what is the reason for this behavior?

MydrecTeni
  • 47
  • 5
  • 1
    That makes you think there's a problem in the second case? You can't predict the order in which the threads execute, but that's all. Note that the "10" at the start of the second line is almost certainly from the *first* part. – Jon Skeet Feb 05 '18 at 11:48
  • 1
    Related https://stackoverflow.com/questions/271440 – Aleks Andreev Feb 05 '18 at 11:50
  • Your first loop runs into the problem discribed by Eric Lippert in [Closing over the loop variable.](https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/). The second one is the way to go. – C. Gonzalez Feb 05 '18 at 12:07

1 Answers1

1

What you did in the second for loop is called closure, it allows anonymous methods and lambda-functions to capture unbound variables in their lexical scope.

Probably, this is the most classic sample cited by everyone:

public void Run()
{
  var actions = new List<Action>();
  for (int i = 0; i < 3; i++)
    actions.Add(() => Console.WriteLine(i));
  foreach (var action in actions)
    action();
}

The sample contains a typical error. Newbie developers think that this code will output "0 1 2", but in fact it will output "3 3 3". Such strange behavior is easy to understand if you look on the expanded version of this method:

public void Run()
{
  var actions = new List<Action>();
  DisplayClass c = new DisplayClass();
  for (c.i = 0; c.i < 3; c.i++)
    actions.Add(c.Action);
  foreach (Action action in actions)
    action();
}

private sealed class DisplayClass
{
  public int i;

  public void Action()
  {
    Console.WriteLine(i);
  }
}

In this case they say that the variable is cycled by reference, not by value. Many programmers criticize this peculiarity of closures. They think it’s unclear though it’s quite logical for those who get a clear idea what’s inside the closures.

Code sample from this website. Here You can read details.

Markiian Benovskyi
  • 2,137
  • 22
  • 29