让你的收藏线程安全吗?

时间:2008-10-02 19:22:27

标签: c# java multithreading collections

在设计集合类时,有没有理由不私下实现锁定以使其线程安全?或者我应该将这个责任交给收藏的消费者吗?

17 个答案:

答案 0 :(得分:13)

  

有没有理由不私下实现锁定以使其线程安全?

这取决于。你的目标是编写一个由多个线程访问的集合类吗? 如果是这样,请使其线程安全。如果没有,不要浪费你的时间。当人们谈论“过早优化”时,人们会提到这种事情

解决您遇到的问题。不要试图解决你认为未来几年可能会遇到的未来问题,因为你看不到未来,你总是错的。

注意:您仍然需要以可维护的方式编写代码,这样如果您 需要出现并为集合添加锁定,那就不会非常困难。我的观点是“不要实现你不需要也不会使用的功能”

答案 1 :(得分:10)

对于Java,您应该保持不同步的速度。如果需要,集合的使用者可以包装synchronization wrapper

答案 2 :(得分:6)

线程安全集合可能是骗人的。 Jared Par发表了一些关于线程安全集合的有趣文章:

  

问题是有几个   线程安全集合的级别。一世   发现当大多数人说线程时   安全收集他们真正的意思   “一个不会的集合   修改和访问时损坏   来自多个线程“

     

...

     

但是如果构建数据线程安全的话   列表是如此简单,为什么不是微软   在中添加这些标准集合   框架?

     

答案:ThreadSafeList是一个   几乎无法使用的课程,因为   设计引领你走向糟糕的道路   代码。

     

这种设计的缺陷不是   直到你检查列表如何才明白   常用。例如,拿   尝试以下代码   抓住列表中的第一个元素   如果有的话。

static int GetFirstOrDefault(ThreadSafeList<int> list) {
    if (list.Count > 0) {
        return list[0];
    }
    return 0; }
  

此代码是典型的竞争条件。考虑只有一个&gt;的情况。列表中的元素。如果另一个线程在if语句和return语句之间删除了该元素,则return语句将抛出异常,因为它试图访问列表中的无效索引。即使ThreadSafeList是数据线程安全的,也没有什么能保证在下一次调用同一个对象时一个调用的返回值的有效性

http://blogs.msdn.com/b/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

http://blogs.msdn.com/b/jaredpar/archive/2009/02/16/a-more-usable-thread-safe-collection.aspx

答案 3 :(得分:3)

集合类需要尽可能快。因此,请将锁定保留。

调用代码将知道集合类最好的锁定位置。在最糟糕的情况下,应用程序必须添加一个额外的锁,这意味着发生了两次锁定,使其达到了性能的两倍。

答案 4 :(得分:2)

在您的文档中明确指出,您的线程不是安全的,并且将其保留,或者,如果您的应用程序需要线程安全,请确保线程安全,并在文档中注明。唯一的规则是记录它。除此之外,为您制作课程,如果其他人想要使用它,他们可以。

答案 5 :(得分:2)

我个人会把它留给消费者。它会使你的集合类更通用。

答案 6 :(得分:2)

如果我正在寻找一个集合类,我需要线程安全功能而你的课程没有它们,我会立即跳到下一个提供它们提供的内容。您的收藏将不再受到我的关注。

注意开头的“如果”。有些客户会想要它,有些则不会,有些则不会关心。如果您要为消费者构建工具包,那么为什么不提供这两种品种呢?这样我可以选择使用哪一个,但如果我想要线程安全,你仍然需要我的注意力,我不必自己写。

答案 7 :(得分:1)

不使线程安全的主要原因是性能。线程安全代码可能比非安全代码慢100倍,因此如果您的客户端不需要该功能,那将是一个非常大的浪费。

答案 8 :(得分:1)

请注意,如果您尝试使任何类线程安全,则需要确定常见的使用方案。

例如,在集合的情况下,只是让所有属性和方法单独地线程安全可能对消费者来说不够好,因为首先读取计数,然后循环或类似,不会做太多如果计数在读完后改变了,那就好了。

答案 9 :(得分:1)

使集合线程安全是杀死Java的Vector和Hashtable类的原因。如前所述,客户端将它包装在线程安全包装中要容易得多,或者在方法子集上同步数据访问,而不是每次访问类时都进行同步命中。几乎没有人使用Vector或Hashtable,如果他们这样做,他们会被嘲笑,因为他们的替换(ArrayList和HashMap)世界更快。这是不幸的,因为我(来自C ++背景)更喜欢“Vector”名称(STL),但ArrayList仍然存在。

答案 10 :(得分:1)

基本上,将您的集合设计为线程安全的,并在您的类的两个方法中实现锁定:lock()和unlock()。在任何需要的地方打电话给他们,但留空。然后继承您的集合,实现lock()和unlock()方法。两个等级的价格为一个。

答案 11 :(得分:1)

不使集合线程安全的一个很好的理由是提高单线程性能。示例:Vector上的ArrayList。将线程安全性延迟到调用者允许通过避免锁定来优化不同步的用例。

使您的集合线程安全的一个非常好的理由是提高多线程性能。示例:HashMap上的ConcurrentHashMap。由于CHM内化了多线程问题,因此它可以比外部同步更有效地对锁定以实现更高的并发访问。

答案 12 :(得分:0)

从JDK 5开始,如果你需要一个线程安全的集合,我首先要看看java.util.concurrent中已经实现的集合之一是否可行。正如 Java Concurrency In Practice 的作者指出的那样(包括编写大部分类的人)正确实现这些是非常困难的,特别是如果性能很重要的话。

引用http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html

  

并发收藏

     

除了队列,这个包供应   集合实现设计   用于多线程上下文:   ConcurrentHashMap中,   ConcurrentSkipListMap,   ConcurrentSkipListSet,   CopyOnWriteArrayList,和   CopyOnWriteArraySet。当很多线程   预计会访问一个给定的   集合,一个ConcurrentHashMap是   通常比同步更好   HashMap和ConcurrentSkipListMap   通常比a更受欢迎   同步的TreeMap。一个   CopyOnWriteArrayList比较好   一个同步的ArrayList时   预期的读数和数量   遍历量大大超过了   列表的更新次数。

答案 13 :(得分:0)

这是一个良好的开端。

thread-safe-dictionary

但是你会发现你失去了收藏的一个重要特征 - 枚举。你不能对枚举器进行线程安全,它只是不可行,除非你实现自己的枚举器,它将实例锁保存回集合本身。我怀疑这会导致严重的瓶颈和潜在的僵局。

答案 14 :(得分:0)

如果您创建了一个集合类,请不要使其成为线程安全的。这样做很难(例如正确和快速),当你做错了(heisenbugs)时,你的消费者的问题很难调试。

实际上,实现一个Collection API并使用Collections.synchronizedCollection(yourCollectionInstance)在需要时获取线程安全的实现。

只需在类javadoc中引用相应的Collections.synchronizedXXX方法;它将清楚地表明您已经在设计中考虑了线程安全性,并确保消费者可以随意使用线程安全选项。

答案 15 :(得分:0)

我同意将其留给消费者是正确的方法。如果为消费者提供了更多的灵活性,以确定是否同步了Collection实例,还是同步了另一个对象。例如,如果您有两个需要更新的列表,那么使用单个锁将它们放在一个同步块中可能是有意义的。

答案 16 :(得分:0)

即使您知道您触摸的元素未被其他任何人使用,也无法同时从多个线程访问集合。

示例是具有基于整数的索引访问器的集合。每个线程可能从其id知道它可以访问哪些索引值而不必担心脏读/写。

另一种会导致不必要的性能损失的情况是,只从集合中读取数据而不写入数据。

相关问题