以线程安全的方式添加到Parallel.ForEach循环中的列表

时间:2013-07-02 02:09:11

标签: c# multithreading

我在一个名为ListofObjects的obj对象列表中有一些像这样工作的代码:

List<SomeObject> NewListofObjects<SomeObject>();

Parallel.ForEach(ListofObjects, obj =>

//Do some operations here on obj to get a newobj

NewListofObjects.Add(newobj);

);

现在我没有使用Parallel.ForEach循环,我想对NewListofObjects进行操作。但是,当我尝试:“尝试读取或写入受保护的内存时,我收到此错误。这通常表示其他内存已损坏”。

这是因为我的NewListofObjects.Add(newobj)方法不是线程安全的吗?如果是这样,我怎样才能使它成为线程安全的?

3 个答案:

答案 0 :(得分:27)

  

这是因为我的NewListofObjects.Add(newobj)方法不是线程安全的吗?

正确。它不是线程安全的。

  

不保证所有实例成员都是线程安全的。

来自MSDN引用List<T>(滚动到标题为“线程安全”的部分)。

  

如果是这样,我怎样才能使它成为线程安全的?

使用并发集合,例如ConcurrentBag<T>。请注意,您无法跟踪项目的插入顺序。

答案 1 :(得分:15)

您可以像以下代码一样使用locking块以线程安全的方式将项目插入列表。

var sync = new object();
var myNewList = new List<SomeObject>();
Parallel.ForEach(myListOfSomethings, a =>
    {
        // Some other code...
        var someObj = new SomeObject();
        // More other code...
        lock(sync)
        {
            myNewList.Add(someObj);
        }
        // Even more code...
    });

答案 2 :(得分:0)

.NET Framework 4引入了System.Collections.Concurrent命名空间,该命名空间包括几个线程安全和可伸缩的集合类。 https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/

      BlockingCollection<int>[] sourceArrays = new BlockingCollection<int>[5];
  for(int i = 0; i < sourceArrays.Length; i++)
      sourceArrays[i] = new BlockingCollection<int>(500);
  Parallel.For(0, sourceArrays.Length * 500, (j) =>
                      {
                          int k = BlockingCollection<int>.TryAddToAny(sourceArrays, j);
                          if(k >=0)
                              Console.WriteLine("added {0} to source data", j);
                      });