【问题标题】:Lock free & Thread-Safe IList<T> for .NET.NET 的无锁和线程安全 IList<T>
【发布时间】:2011-02-21 19:46:07
【问题描述】:

是否有实现 IList 的无锁且线程安全的数据结构?

当然,无锁是指一种实现,它不使用 .NET 中的锁定原语,而是使用互锁操作/原子操作来实现线程安全...... 没有,显然是在并发数据结构下...

有没有人看到一个漂浮的?

我见过在amino-cbbs 中实现的java,称为LockFreeVector,但到目前为止还没有用于.NET。 有什么想法吗?

【问题讨论】:

  • 我想你的意思是“无锁”和线程安全,因为List&lt;T&gt; 是完全无锁的。您还应该澄清“无锁”的含义。
  • @damageboy 另一个注意事项:他们(氨基)正在实现一个 LINKED 列表,而不是一个列表。 C# 中的 LinkedList 不实现 IList/IList。他们有一个 LockFreeVector ......但我不认为它是“完全”无锁的。
  • 您要寻找的实现有多“完整”?我非常怀疑您是否能够找到,例如,一种支持随机 Insert 而没有锁定的类型(除非您允许 spinning,我猜,因为这与“锁定”)。但是,我知道什么?
  • @xantos:我指的是氨基的 LockFreeVectoramino-cbbs.sourceforge.net/java_apidocs/org/amino/ds/lockfree/…
  • @damageboy:请注意,LockFreeVector&lt;E&gt; 所基于的论文中描述的无锁结构 not 具有随机的 Insert 操作(这是IList&lt;T&gt; 接口)。

标签: .net multithreading structure ilist lock-free


【解决方案1】:

嗯,我在任何地方都找不到这样的课程;所以I gave it a shot

我的ConcurrentList&lt;T&gt; 类的源代码是available on GitHub

它是无锁的、线程安全的(我认为,基于我的单元测试),并实现了IList&lt;T&gt;

支持InsertRemoveAt/RemoveClear

我很高兴地发现我的实现(我独立提出)与软件世界中的数据结构 published by some well-respected minds 非常相似。

有关实现本身的相当简短的讨论,请参阅my recent blog post

目前,它根本没有记录,考虑到某些代码有多“棘手”,这有点糟糕:(

如果您查看并发现错误或其他问题,请务必给我一个新的。

无论如何,值得您花时间检查一下。如果你这样做了,请告诉我你的想法。

【讨论】:

  • +1 表示继续并自行实施。我也在将 java 版本移植到 c# 中...我阅读了您的博客文章,我仍将在我的机器上运行您的测试,但主要好处是单个编写器的多个读取器线程/附加线程。这就是您应该期望看到性能提升的地方,因为简单的“使用锁()”解决方案仍然会为每次读取锁定......
  • 顺便说一句:LockFreeVector 的氨基-cbbs 版本也基于您引用的同一篇论文:amino-cbbs.svn.sourceforge.net/viewvc/amino-cbbs/trunk/amino/…
  • 我非常喜欢你的实现。我已经对其进行了一些改进,我会将其上传到 github,以便您也可以查看它们。我所做的大部分更改都围绕着具有更多逻辑初始数组大小,例如8,16,32...而不是 1,2,4(有点浪费 IMO),以及将您的 LOG2 功能改进为完全可怕的东西,应该在更少的周期内运行...
  • C# 中有 volatile 关键字吗? CAS 的目标在 C 中需要是可变的,因为 CAS 是否成功是运行时的决定。
  • 同样,你如何确保对齐? x86/x64 上的 CAS 需要字对齐数据;非字对齐会导致异常。
【解决方案2】:

由于整个 Parallel.ForParallel.ForEach 类方法,Collections.Concurrent 命名空间中可能缺少实现 IList 的 ConcurrentList。可以说它们可用于将任何列表作为并发处理,以便快速枚举列表并对其项目执行操作。

也许通过不提供ConcurrentList,他们的意思是或认为如果 Parralel.For 无法提供帮助,则需要使用的不是 IList,而是其他类型的集合,例如堆栈或队列,甚至是 Bag 甚至是 Dictionary

我同意这种设计,因为在多线程条件下必须处理可索引集合听起来很容易出错和糟糕的设计。如果可以随时修改集合并且索引将失效,那么了解项目索引的目的是什么?包包也可以。也可以使用字典,因为它的索引不会通过将项目添加到集合中而失效,并且如果您需要并行访问 List,您将获得 Parralel.For 方法

我觉得很奇怪 - http://msdn.microsoft.com/en-us/library/dd381935.aspx 在这里我们可以阅读 ConcurrentLinkedList 类,但我在 System.dll 中找不到它,只有 Bag 和 BlockingCollection 在那里。

我还要说,至少有 95% 的机会你的问题有两个是真的

  1. 并行类方法更好 比并发列表
  2. 现有 并发集合将 比收藏更好 并发列表

我还要说,通过不提供 ConcurrentList,他们可以帮助开发人员避免错误地选择 ConcurrentList 来解决他们的问题,从而避免犯很多错误,并为他们节省大量时间,迫使开发人员使用现有的 Concurrent 集合。

【讨论】:

  • 您站点的页面上说 ConcurrentLinkedList 将在 VS 2010 发布之前被删除并且不要使用它。我同意你写的一切 - 干得好 +1。
  • 虽然有很多点我确实同意你的观点,但我仍然认为需要一个线程安全的 IList,就像氨基 cbbs 中提供的 LockFreeVector ...主要的用例,巧合的是我所追求的,是当您从一个/多个线程对 List 进行仅追加修改而其他一些线程可能需要时当他们开始枚举/处理时,以只读方式遍历列表直到最后一个已知的 .Count 值...... IOW,我正在寻找与数组等效的只读性能,同时仍然是可以追加
【解决方案3】:

想想随机访问(由IList&lt;T&gt; 暗示)如何在多线程环境中工作。如果它是不可变的,您实际上无法真正做任何事情,因为添加和删除,即使它们是原子的,也不要阻止线程 1 删除线程 2 即将检索的索引处的项目。这就是 .NET 2.0+ 中没有 SyncRoot 的原因

【讨论】:

  • 当 Valentin Kuzub 在他的评论中提出这一点时,我已经考虑过并且我已经回答了这一点。唯一有用的场景是 .Add() only IList,其中删除/插入不适用/不允许。这可以而且确实可以极大地提高读取器性能,同时仍然允许线程安全的附加操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-30
  • 1970-01-01
  • 2012-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多