并行任务执行的不同行为与并发字典中的删除元素

时间:2017-09-14 12:35:59

标签: c# multithreading concurrency thread-safety task

我想测试并行添加和删除到并发字典。 我的测试类中有以下代码:

[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());
    }

添加和删除方法:

  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);
    }

这完全没问题。但是当我开始删除奇怪的事情时(忽略int区域,我不知何故认为它们是问题的一部分,但它们不是)

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

        }

如果每个任务都相同,我总会获得相同的地址和输出。

如果我使用以下代码,请执行以下操作:

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])));

它没有问题。如果我在for循环中添加一些虚拟代码或Thread.Sleep(1),它也可以工作(每个任务获得不同的地址)

有人可以解释这种行为吗?

1 个答案:

答案 0 :(得分:1)

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

没有做你认为的事情。

您应将其更改为:

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

使用原始代码,在Task运行时,i的值已更改。所以你需要将它存储在一个单独的变量(bob - 作用于循环内部),以便第一个Task得到0,下一个获得1等。

您的问题与this one基本相同(尽管使用foreach原则相同)。

请注意,如果您安装了Resharper,那么您应该highlighted这个问题。