在不锁定集合的情况下从通用集合中获取 Count 值是不是安全?

Posted

技术标签:

【中文标题】在不锁定集合的情况下从通用集合中获取 Count 值是不是安全?【英文标题】:Safe to get Count value from generic collection without locking the collection?在不锁定集合的情况下从通用集合中获取 Count 值是否安全? 【发布时间】:2010-11-24 01:17:24 【问题描述】:

我有两个线程,一个将对象放入通用列表集合的生产者线程和一个将这些对象从同一个通用列表中拉出的消费者线程。我已经使用 lock 关键字正确同步了对集合的读取和写入,并且一切正常。

我想知道的是是否可以在不首先锁定集合的情况下访问 Count 属性。

JaredPar 将 Count 属性 in his blog 称为可能导致竞争条件的决策过程,如下所示:

if (list.Count > 0)

    return list[0];

如果列表有一个项目,并且在访问 Count 属性之后但在索引器之前删除了该项目,则会发生异常。我明白了。

但是可以使用 Count 属性来确定一个完全不同的集合的初始大小吗? MSDN documentation 表示不能保证实例成员是线程安全的,所以我应该在访问 Count 属性之前锁定集合吗?

【问题讨论】:

【参考方案1】:

怀疑就“它不会导致任何灾难性错误”而言,它是“安全的”——但您可能会得到陈旧的数据。那是因为我怀疑它只是保存在一个简单的变量中,而且将来很可能会出现这种情况。但是,这与保证不同。

我个人会保持简单:如果您正在访问共享的可变数据,请仅在锁定中进行(对相同的数据使用相同的锁定)。如果您有适当的隔离,那么无锁编程会非常好(所以您知道您有适当的内存屏障,并且您知道在读取它时永远不会在一个线程中修改它在另一种情况下),但听起来情况并非如此。

好消息是,获得无争议的锁非常便宜 - 所以如果我是你,我会选择安全的路线。线程在不引入竞争条件的情况下已经足够困难,竞争条件可能不会带来显着的性能优势,但会以罕见且不可重现的错误为代价。

【讨论】:

@Jon Skeet:伙计,我很幸运有你参与其中!在这种情况下,一个陈旧的值是完全可以的,因为它只是用来确定另一个集合的“粗略”初始大小。我买不起的是 Count 属性返回负值或非常大的值。如果在我拉取 Count 值的同时另一个线程正在修改集合,是否有可能发生这种情况? Count 是一个 int,所以即使被修改,它也会原子地完成,对吧? @Matt:我期望这没问题 - 但我仍然会在我自己的代码中安全地使用它。您将依赖可能在以后的版本中更改的实现细节。是的,它可能只是存储在一个简单的int 变量中,但你真的想依赖它吗?您关心性能还是代码简单性?如果您使用 C# 3 并锁定集合本身,则可以创建一个扩展方法(“LockedCount()”或类似的东西)以使其更容易一些。 @Jon Skeet:你是对的。如果不能保证其当前和未来的实施,猜测是不明智的。非常感谢! @JonSkeet LockedCount,好主意,已经在我的项目中实现了

以上是关于在不锁定集合的情况下从通用集合中获取 Count 值是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有动画的情况下从集合视图中删除项目?

在 Linq 中,如何在不使用 Count(predicate) 的情况下查找集合是不是包含元素?

如何让 Json.Net 在不忽略子属性的情况下从 documentDB 序列化/反序列化动态/通用对象?

使用 Mongoose 查找和计数集合元素

如何从集合中检索元素而不删除它?

在不使用地图 android 的情况下从邮政编码中获取纬度和经度