-1

I wanted to test parallel Adding and Removing to a Concurrent Dictionary. I have the following code in my test class:

[TestMethod]
    public void T_Parallel_Removing
    {
        var management = new Management();

        List<Task> AddTasks = new List<Task>();
        List<Task> RemoveTasks = new List<Task>();

        var units = new List<Unit>();

        for (int i = 0; i < 30; i++)
        {
            var unit= = new Unit();
            units.Add(unit);
            AddTasks.Add(Task.Run(() => AddUnit(management, unit)));
        }

        Task.WaitAll(AddTasks.ToArray());
    }

The Add und remove Methods:

  public void AddUnit(Management m, Unit u,)
    {
        management .AddPlantUnit(u, "",);
    }


    public void RemoveUnit(Management m, Unit u)
    {
        m.RemovePlantUnit(u.Adress);
        Console.WriteLine(u.Adress);
    }

This works perfectly fine. But when i start Removing something odd happens (ignore the int areas, i somehow thought that they are a part of the problem, but they are not)

for (int i = 0; i < 29; i++)
        {
          RemoveTasks .Add(Task.Run(() => RemoveUnit(management, units[i])));

        }

Here i get always the same adress and the output if for every task the same.

BUt if i use following code:

RemoveTakes.Add(Task.Run(() => RemoveUnit(management, units[0])));
         RemoveTasks .Add(Task.Run(() => RemoveUnit(management, units[1])));
         RemoveTasks .Add(Task.Run(() => RemoveUnit(management, units[2])));
         RemoveTasks .Add(Task.Run(() => RemoveUnit(management, units[3])));
         RemoveTasks .Add(Task.Run(() => RemoveUnit(management, units[4])));
         RemoveTasks .Add(Task.Run(() => RemoveUnit(management, units[5])));

It works without a Problem. If i add some dummy Code or a Thread.Sleep(1) into the for loop it also works (every task gets a different adress)

Can someone explain this behaviour?

Rolfant90
  • 3
  • 3
  • If you change `Task.Run(() => RemoveUnit(management, units[i]))` to `Task.Run(() => Console.WriteLine(i))` what is written to the console? Is that what you expected? – mjwills Sep 14 '17 at 12:42
  • Without knowing about [closures](https://stackoverflow.com/q/428617/1997232) nearly any lambda in the loop = [problem](https://stackoverflow.com/q/16264289/1997232). – Sinatr Sep 14 '17 at 12:59

1 Answers1

1
for (int i = 0; i < 29; i++)
{
  RemoveTakes.Add(Task.Run(() => RemoveUnit(management, units[i])));
}

doesn't do what you think it does.

You should change it to:

for (int i = 0; i < 29; i++)
{
  var bob = i;
  RemoveTakes.Add(Task.Run(() => RemoveUnit(management, units[bob])));
}

With your original code, by the time the Task ran the value of i had changed. So you need to store that in a separate variable (bob - scoped inside the loop) so that the first Task gets 0, the next gets 1 etc.

Your issue is basically the same as this one (although that uses a foreach the principle is the same).

Note that if you have Resharper installed it should have highlighted this issue for you.

mjwills
  • 22,686
  • 6
  • 37
  • 60