并发数据结构的使用示例?

时间:2015-03-24 16:03:01

标签: java c# multithreading data-structures concurrency

最近有很多关于并发和多线程的讨论,并且有充分的理由,但我在查看实际应用时遇到了麻烦。

就像,.NET最近添加了ConcurrentDictionaryConcurrentBagConcurrentStackConcurrentQueue等。

什么时候会有什么具体的实际例子?理想情况下,我喜欢一些容易相关的场景,其中非并发的一个会失败,或者并发的一个会因为易于制作并行而快得多。

我主要对ConcurrentDictionary感兴趣,如果这样可以更轻松地提供任何示例。当我想到它时,我不明白它是如何并行可以帮助提高速度。

我们假设您有一个您希望添加的数据源。不会跨越分布在8个核心上的8个线程对数据源进行迭代,这与单个线程的速度相似,因为在任何一种情况下,您都是从一端开始到另一端,并且使用同步的那个在添加已经添加的数据时不会有任何争用吗?

基本上,我想了解并发性如何使程序更快,以及在非并发实现失败的情况下,因为我现在似乎无法想到许多它们在这方面有用的例子。 (主要语言C#)

2 个答案:

答案 0 :(得分:3)

通常,并发数据结构的添加速度比不支持并发修改的数据结构慢。 如果您一次只添加一件事。也就是说,如果您的代码只是一个执行此操作的线程:

foreach (var item in list)
{
    AddItemToDataStructure(item);
}

如果数据结构为Dictionary,则运行速度会比ConcurrentDictionary更快。

但如果您有多个线程针对ConcurrentDictionary执行此操作,则执行可以提高速度。更新字典涉及的许多工作涉及确定项目的去向以及具有该密钥的项目是否已存在。这项工作可以由多个线程同时完成。只有最终插入(即更新内部数据结构)才需要某种同步以防止数据结构的损坏。而且这种同步在大多数情况下都是非常小的。

(以上是一个简化的解释,但我认为你明白了。)

至于ConcurrentQueueConcurrentStack,他们并不是真的可以让事情更快,而是让事情成为可能 。比如说,你有两个线程从一些外部源读取数据,一个线程处理读取的数据。你有两个生产者和一个消费者。它们通过共享队列进行通信。所以你拥有的是两个执行此操作的线程:

while data is available
    read data
    add data to shared queue

一个线程就是这样做的:

while not end of program
    while data is in queue
        read data from queue
        process data

如果用锁来包装这些数据结构,那么非并发数据结构(即QueueStack)是可能的,但是很难得到它对。如果你想消除轮询循环,那就更难了。并发数据结构可以为您完成所有这些。

结果是使用ConcurrentQueue最有可能比围绕Queue包装简单的同步包装更快。

通过允许多个线程同时执行不同或相关的任务,并发使程序更快。通过在线程之间提供可靠的通信,并发数据结构促进。此外,并发数据结构通常会优于包含在简单同步包装器中的相应非并发数据结构。

答案 1 :(得分:0)

在Java HashMap中,在没有足够锁定的情况下并发使用时会中断。请参阅Is a HashMap thread-safe for different keys?以获得一个好的答案。

在Java中,ConcurrentSkipListSet使用Skip List数据结构,因此可以同时更新和迭代多个线程,而不会破坏而不使用锁。因此,它比在普通Set上实施锁定制度更快