【问题标题】:Multiple writers, one reader, which collection多个作家,一个读者,哪个收藏
【发布时间】:2013-02-01 02:37:23
【问题描述】:

我的情况是这样的:

多个线程必须同时写入同一个集合(add 和 addrange)。物品的顺序不是问题。 当所有线程都完成(加入)并回到我的主线程时,我需要以 foreach 样式快速读取所有收集的数据,因为所有线程都已完成,因此不需要实际锁定。

在“过去”中,我可能会在列表上为此使用读写器锁,但对于新的并发集合,我想知道是否有更好的选择。我只是不知道是哪个,因为大多数并发集合似乎都假设读者也在并发线程上。

【问题讨论】:

  • 如果您使用的是 .Net 4.0 或更高版本。 ConcurrentBag 应该可以解决您的目的。如果您不关心元素的顺序和删除,我认为这是最好的。
  • 我一直在研究它,但不会对并发包进行 foreach 意味着每次读取都会受到锁定惩罚吗?让老读者锁更高效?
  • @Hans 和 Philip Rieck,你说得对。这里根本没有理由使用并发集合。现在对我来说很明显,我必须旋转很多线程,这让我感到盲目和偏执。谢谢澄清:)。

标签: c# multithreading concurrency


【解决方案1】:

我认为您不想使用 System.Collections.Concurrent 中的任何集合。这些通常有额外的开销以允许并发读取。

除非您有很多争用,否则您最好锁定一个简单的List<T> 并添加它。当列表调整大小时,您将有少量开销,但这种情况很少见。

但是,在这种情况下,我可能会做的只是添加到 List<T> 每个线程 而不是共享的,然后在处理结束时合并它们,或者简单地迭代每个集合中的所有元素。

您可以使用ConcurrentBag,然后在准备读取时调用.ToArray()GetEnumerator()(绕过每次读取的惩罚),但您可能会发现插入速度有点慢比你在一个简单的List 上手动写锁。这实际上取决于争用的数量。 ConcurrentBag 非常适合分区,但正如您所指出的,它适用于并发读取写入。

与往常一样,对您的特定情况进行基准测试!多线程性能在实际使用中高度依赖于许多因素,而数据类型、插入次数等因素会极大地改变结果 - 少数现实值得一加仑理论。

【讨论】:

  • +1 推荐基准测试。菲利普是对的,你真的想知道你的实际数据/读/写行为如何。
【解决方案2】:

项目的顺序不是问题。当所有线程都完成(加入)并回到我的主线程时,我需要读取所有收集的数据

您根本没有声明对线程安全集合的要求。共享一个集合是没有意义的,因为您从来没有在写作的同时阅读。所有写入都发生在同一个集合上也没关系,因为顺序无关紧要。这也不重要,因为无论如何顺序都是随机的。

所以只需给每个线程自己的集合来填充,不需要锁定。然后一个一个地迭代它们,不需要锁定。

【讨论】:

    【解决方案3】:

    试试System.Collections.Concurrent.ConcurrentBag

    来自集合的描述:

    表示线程安全、无序的对象集合。

    我相信这符合您处理多个线程和不重要的项目顺序的标准,稍后当您回到主线程时,您可以快速 foreach 迭代集合并对每个项目采取行动。

    【讨论】:

    • 我一直在研究它,但是并发包上的 foreach 不会意味着每次读取都会受到锁定惩罚吗?让老读者锁更高效?
    • @ChristianMikkelsen 您运行的是 .NET 4.0 还是 .NET 4.5?以下博客条目讨论了ConcurrentBag 的性能:ayende.com/blog/156097/…
    • @ChristianMikkelsen 不,你应该没问题: ConcurrentBag.GetEnumerator() 在那个时候返回一个快照,所以它不会锁定集合。见:msdn.microsoft.com/en-us/library/dd381960.aspx
    猜你喜欢
    • 1970-01-01
    • 2015-05-05
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    相关资源
    最近更新 更多