线程安全是否使用concurrentbag创建任务

时间:2016-06-25 03:18:08

标签: c# multithreading

我有一个ConcurrentBag集合,对于这个中的每个项目,我都会做一些工作。

我知道带ConcurrentBag的Parallel.ForEach是线程安全的,但我从未以这种方式完成并行操作。

我的代码看起来像这样

public void Work(IList<MyModel> data)
{
   var tasks = new ConcurrentBag<Task>();
   var safeData = new ConcurrentBag<MyModel>(data);
   foreach (var item in safeData )
   {
      var task = Task.Factory.StartNew(() => SomeTask(item));
      tasks.Add(task);
   }
   Task.WaitAll(tasks.ToArray());
}

那么,这段代码是线程安全的吗?

由于

修改 也许这个问题可能是:“item”是线程安全的还是可以在迭代之间改变它的值?

编辑2: 如果此代码可能属于此problem,我会引用。

编辑3: 在这个question中,“Wonko the sane”在他直接使用循环变量时遇到了问题。我认为使用ConcurrentBag可以解决这个问题,但在某些答案中,告诉我根本不需要ConcurrentBag。所以:

直接使用循环变量是个好主意吗?

何时推荐将值复制到另一个变量?

直接使用ConcurrentBag中的循环变量是否安全?

2 个答案:

答案 0 :(得分:3)

您完全不需要ConcurrentBag代码....而且您ConcurrentBag甚至不需要Parallel.ForEachParallel.ForEach的参数不需要是线程安全的。

相反,你可以简单地写一下,

public void Work(IList<MyModel> data)
{
   Parallel.ForEach(data, item=>{
       DoSomeTask(item);
   });
}

...交替地

public void Work(IList<MyModel> data)
{
   Task.WaitAll(data.Select(item=>{
       DoSomeTask(item);
   }));
}

只有在ConcurrentBag内执行的代码中对其进行修改时才需要DoSomeTask

每个item是每个任务的独立实例,除非您在集合中有重复的项目。即使您在输入列表中有重复的项目,ConcurrentBag也无法解决任何问题。

答案 1 :(得分:1)

除现有答案外:

问题不在于多线程或并发。这是关于closures

  1. 如果您了解正在发生的事情,可以直接使用循环变量。
  2. 如果是foreach,则取决于您的编译器版本。如果代码将使用旧版本编译,则会导致问题,如that very question中所述。如果使用for循环,则应始终复制循环变量。有关详细信息,请参阅Jon's answer
  3. 中的链接
  4. 如前所述,使用ConcurrentBag不会影响结果 如果您的编译器版本低于4.0.30319.1(在MS编译器的情况下),则可以安全地关闭循环变量。 More info about compiler versions
  5. 同样如该问题的评论中所述,您可以使用静态分析工具来强调此类情况。