使用ConcurrentBag类型作为List的线程安全替代

时间:2014-08-06 21:58:29

标签: c# collections

在genereal中,使用ConcurrentBag类型是List可接受的线程安全替代吗?我在这里已经阅读了一些建议,当C#中的通用列表出现线程安全问题时,建议使用ConcurrentBag

然而,在阅读了一些关于ConcurrentBag的内容之后,似乎执行了大量搜索并且在集合中循环与其预期用法不匹配。它似乎主要是为了解决生产者/消费者问题,即从集合中添加和删除工作(有些是随机的)。

这是我想要与IEnumerable一起使用的(ConcurrentBag)操作类型的示例:

...
private readonly ConcurrentBag<Person> people = new ConcurrentBag<Person>();

public void AddPerson(Person person)
{
   people.Add(person);
}

public Person GetPersonWithName(string name)
{
   return people.Where(x => name.Equals(x.Name)).FirstOrDefault();
}
...

这是否会导致性能问题,甚至是使用ConcurrentBag集合的正确方法?

4 个答案:

答案 0 :(得分:3)

.NET内置的并发数据结构最常用于生产者 - 消费者等模式,其中通过容器的工作量不断增加。

在您的情况下,列表似乎是长期的(相对于类的生命周期)存储,而不是仅仅是一些数据的休息位置,然后消费者才会将其带走并对其执行某些操作。在这种情况下,我建议使用普通List<T>(或者哪种非并发集合最适合您正在进行的操作),并使用lock来控制访问权限它。

答案 1 :(得分:1)

Bag是最常见的收集形式,允许多个相同的条目,甚至没有List的排序。它确实在生产者/消费者环境中有用,其中公平性不是问题,但它不是专门为此设计的。

因为Bag的内容没有任何结构,所以它不适合进行搜索。特别是,您提到的用例需要的时间与包的大小成比例。如果您不需要能够存储项目的多个副本,并且如果您的用例可以接受手动同步,则HashSet可能会更好。

答案 2 :(得分:1)

据我所知,ConcurrentBag使用了多个列表。它使用ConcurrentBag为每个线程创建一个List。因此,当再次在同一个线程中读取或访问ConcurrentBag时,性能应该与使用普通List时的性能大致相同,但如果从不同的线程访问ConcurrentBag,则会产生性能开销,因为它必须搜索为每个线程创建的“内部”列表中的值。

MSDN页面就ConcurrentBag说明了以下内容。

  

当订购无关紧要时,包对于存储物品很有用,与套装不同,包支持重复。 ConcurrentBag是一个线程安全的包实现,针对同一个线程生成和使用存储在包中的数据的场景进行了优化。

http://msdn.microsoft.com/en-us/library/dd381779%28v=VS.100%29.aspx

答案 3 :(得分:0)

  

在genereal中,使用ConcurrentBag类型是List的可接受的线程安全替代吗?

不,一般情况下,因为,与Warren Dew一致,订购的是List,而Bag则不是(肯定是我的不是;)

但是在(可能并发)读取数量大大超过写入的情况下,您可以copy-on-write wrap your List

这是一个通用的解决方案,因为您正在处理原始List实例,除了(如上面链接中更详细的解释),您必须确保修改List的每个人都使用适当的写时复制实用程序方法 - 您可以使用List.AsReadOnly()强制执行。

在高度并发的程序中,与锁定相比,copy-on-write在大多数读取方案中具有许多理想的性能属性。