【问题标题】:List<T> concurrent removing and addingList<T> 并发删除和添加
【发布时间】:2010-12-21 06:39:09
【问题描述】:

我不太确定,所以我想我会问。将项目删除和添加到 System.Collections.Generic.List&lt;&gt; 对象是非线程安全的吗?

我的情况:

当接收到一个连接时,它被添加到列表中,但同时,还有一个工作人员正在删除死连接等。

有问题吗? lock 会吗? 我还想知道是否允许我通过Foreach&lt;&gt; 方法对列表对象使用锁定。

【问题讨论】:

标签: c# .net list concurrency


【解决方案1】:

是的,在 List&lt;&gt; 中添加和删除项目不是线程安全的,因此您需要同步访问,例如使用 lock

请注意lock 关键字绝不会锁定您用作标识符的对象,它只会阻止两个线程同时进入同一个代码块。您将需要锁定访问列表的所有代码,使用相同的对象作为标识符。

【讨论】:

  • List.Foreach&lt;&gt;中删除这样的呢,还需要锁吗?
  • @AJ:是的,您需要锁定列表的 all 访问权限。如果您正在循环遍历列表并且另一个线程删除了一个项目,您将得到一个异常(或在某些情况下崩溃)。
  • 这个答案几乎涵盖了“是的,您需要锁定访问列表的所有内容”的情况。最好的办法是为列表提供一个包装器,并锁定包装器中的每个方法。
  • @Guffa 是并发问题,添加的项目可能不在我们怀疑的索引中?如果我不关心我的项目的顺序,我还需要锁定列表吗?
  • @eranotzap:不,并发问题在于List 对象中的内部变量。如果两个线程同时添加项目,内部变量可能会损坏,因为List 类中的代码没有考虑到多个线程。因此,当多个线程更改列表时,您总是需要同步代码。
【解决方案2】:

当时还没有 .NET Framework 4,但是现在遇到问题的人应该尝试使用 System.Collections.Concurrent 命名空间中的集合来处理线程安全问题

【讨论】:

    【解决方案3】:

    List&lt;T&gt; 不是线程安全的,所以是的,您需要使用锁来控制对列表的访问。如果您有多个线程访问列表,请确保它们都尊重锁,否则您将遇到问题。最好的方法是对 List 进行子类化,以便自动发生锁定,否则您很可能最终会忘记。

    【讨论】:

    • 这不是最好的方法。这不仅仅是因为 List 的实现——它的接口也不是为多线程使用而设计的。如果你想要一种线程安全的方式来做到这一点,你也需要一个完全不同的接口。列表应该是内部实现细节,对用户隐藏得很好。 blogs.msdn.com/jaredpar/archive/2009/02/11/…
    【解决方案4】:

    绝对对特定代码使用锁定使其线程安全,但我不同意当前场景。

    您可以实现方法 Synchronized 以使收集线程安全。这个link 解释了为什么以及如何做到这一点。

    link 中提到了另一种纯粹的编程方法,虽然我从未亲身测试过,但它应该可以工作。

    顺便说一句,更大的担忧之一是,您是否试图自己维护连接池之类的东西?如果是,那为什么?

    我收回我的回答。使用锁比使用这种方法更好。

    【讨论】:

    【解决方案5】:

    实际上,根据Microsoft,有时 List 是线程安全的,有时则不是:

    这种类型的公共静态成员是 线程安全。任何实例成员都是 不保证是线程安全的。

    但该页面继续说:

    通过集合枚举是 本质上不是线程安全的 程序。在极少数情况下 枚举与一个或多个竞争 写访问,唯一的方法来确保 线程安全就是锁定 在整个收集 枚举。允许集合 被多个线程访问 阅读和写作,你必须 实现您自己的同步。

    【讨论】:

    • 不,列表永远不是线程安全的。这只是用于所有常规课程的标准文本。由于 List 类根本没有任何静态成员,因此没有线程安全成员。
    • 我同意他绝对应该使用锁。感谢您的澄清。
    • @Guffa:我很确定List&lt;T&gt; 在存在多个读取器和零写入器的情况下是线程安全的。在存在多个阅读器的情况下,集合不是线程安全的,在没有文档的情况下假设这样的线程安全级别是非常罕见的。尽管如此,我要说的唯一“从不”线程安全的集合是诸如惰性集合之类的东西,其中两个同时“读取”访问可能会导致修改冲突。
    • @supercat:你没有抓住重点。如果您从不更改列表,则线程安全不是问题,但这并不意味着该列表“有时”是线程安全的。问题所涉及的操作永远不是线程安全的。
    • @Guffa:如果两个线程尝试同时读取某些类型的集合,无论是否有任何线程尝试写入它们,它们都可能会失败。 List&lt;T&gt; 集合保证一个线程的读取不会以任何会干扰另一个状态的读取的方式改变其状态。碰巧的是,在目前的实现中,读取根本不会改变其状态,但未来的实现理论上可以让Remove 设置一个“已删除”标志并将实际删除推迟到下一次读取以后的项目时。然后需要一个这样做的实现......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-07
    • 2019-07-02
    • 1970-01-01
    相关资源
    最近更新 更多