【问题标题】:Periodically iterating over a collection that's constantly changing定期迭代不断变化的集合
【发布时间】:2010-05-04 04:39:49
【问题描述】:

我有一个不断变化的对象集合,我想不时显示一些有关内容的信息(我的应用程序是多线程的,不同的线程不断提交修改集合中对象的请求,所以这是不可预测的)。

如果我锁定集合,我可以遍历它并毫无问题地获取我的信息 - 但是,这会导致其他线程出现问题,因为它们可能同时提交了多个修改集合的请求,并且将停滞不前。我想了几种方法来解决这个问题,我正在寻找任何建议。

  • 制作集合的副本并对其进行迭代,允许原始集合在后台继续更新。集合可能会变大,所以这并不理想,但很安全。
  • 使用 For...Next 循环对其进行迭代,如果在我们迭代时从集合中删除了一个项目,则捕获 IndexOutOfBounds 异常。这可能偶尔会导致重复出现在我的快照中,因此也不理想。

还有其他想法吗?我只关心即时快照,所以我不关心反映我的应用程序中的变化——我主要关心的是集合能够以最小的延迟进行更新,并且更新永远不会丢失。

【问题讨论】:

  • 我能得到反对票的解释吗?
  • 我同意,任何投反对票的人都必须发表一些评论,我曾多次写信给 SO,要求必须发表评论,但 SO 不听,而且我看到主要的缺乏经验的自我膨胀的程序员来投反对票而没有任何理由,您的问题都是有效的问题。

标签: .net multithreading collections iteration


【解决方案1】:

如果您使用的是 .NET Framework 4,您可能希望考虑使用来自 System.Concurrent 命名空间的一些并发集合。例如,从 ConcurrentQueue<T> 类返回的迭代器代表一个 即时 em> 视图的集合,不受集合变化的影响。 Normal 集合迭代器将因基础集合的更改而失效。否则,你别无选择,只能先锁定收藏。也许有并发集合的第三方实现。但我没有调查过这些。以下是有关 .NET Framework 4 中线程安全集合的信息。

http://msdn.microsoft.com/en-us/library/dd997305(v=VS.100).aspx

【讨论】:

  • 我没有意识到存在这些新类型的集合 - ConcurrentBag(Of T) 可能正是我正在寻找的。感谢您的链接。
【解决方案2】:

我倾向于使用您的第一个选项,使用 .ToArray() 创建一个数组并对其进行迭代。您是否对它进行了分析以查看它制作副本的速度有多慢?对我来说,它通常可以忽略不计,即使对于大型收藏也是如此。

【讨论】:

  • 我还没有对复制操作进行计时以查看它是否会成为瓶颈,但我认为它会是因为我只需要每个对象的几个属性,但对象可能很复杂并且有多个分层(它们本身包含集合)。不过,我会做一些测试,看看 .ToArray 是否足够快来解决我的问题。
  • 多线程环境下我也用过toarray,因为你可以用lock快速复制,而且你对array的迭代操作不会阻塞所有其他线程。
  • 听起来你不需要深拷贝,所以 ToArray 对你来说应该很快,因为它只复制对象引用。
【解决方案3】:

制作集合的副本通常需要您先锁定集合,因此与仅锁定并迭代它相比,这里没有任何好处 - 除非您的集合支持某种快速克隆。

我认为另一种选择是使用不同类型的集合,可以更好地支持并发访问,或者能够快速返回快照。此处的另一个答案链接到.net 特定的答案;如果您有兴趣自己实现一个,我建议您阅读这篇论文:

http://www.cs.tau.ac.il/~shanir/concurrent-data-structures.pdf

【讨论】:

  • 根据您在迭代时所做的事情,首先制作副本然后进行迭代可能是一个很大的收获。如果对集合中每个项目的操作都需要一些时间,那么您将获得更短的锁定。
猜你喜欢
  • 1970-01-01
  • 2012-02-28
  • 2019-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-17
  • 1970-01-01
相关资源
最近更新 更多