【问题标题】:Adding to a list in a Parallel.ForEach loop in a threadsafe manner以线程安全的方式添加到 Parallel.ForEach 循环中的列表
【发布时间】:2013-07-02 02:09:10
【问题描述】:

我有一些代码在名为 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) 方法不是线程安全的吗?如果是这样,我怎样才能使它成为线程安全的?

【问题讨论】:

  • 你在里面做什么操作(从obj到newobj)?
  • 一大堆难以描述的操作......但它们都是在对象列表的每次迭代中创建的局部变量。不过你问这个是对的……我没有排除我对对象所做的所有操作也可能是这个特定问题的根本原因。

标签: c# multithreading


【解决方案1】:

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

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

不保证任何实例成员都是线程安全的。

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

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

使用并发集合,例如ConcurrentBag&lt;T&gt;。请注意,您将无法跟踪项目插入的顺序。

【讨论】:

  • 幸运的是,在我的情况下,项目的顺序并不重要。谢谢!
  • 根据您的需要,您可以使用 ConcurrentBag 进行处理,然后执行 var list = bag.OrderBy(p =&gt; p.SomeCondition).ToList(); 之类的操作,这可能比锁定每个列表添加执行得更好。
【解决方案2】:

您可以使用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...
    });

【讨论】:

  • 我希望我可以将多个答案标记为正确,因为这也可以解决问题。谢谢!
  • 没问题!此解决方案也保留了添加项目的顺序,但在您的情况下,无论哪种方式都可以。 Jason 的方法性能更好。
  • 有什么理由不锁定 myNewList 吗?
  • 您绝对可以直接锁定“myNewList”而不是使用辅助对象变量,只要您可以保证没有其他代码(如客户端代码可以访问的公共方法)将访问同一个对象当它被锁定时。然后,客户端应用程序将在等待列表对象解锁时被完全阻止。这也可能有问题。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多