安全从通用集合中获取Count值而不锁定集合?

Mat*_*vis 10 .net c# multithreading locking .net-3.5

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

我想知道的是,如果没有先锁定集合就可以访问Count属性.

JaredPar将他博客中的Count属性称为可导致竞争条件的决策程序,如下所示:

if (list.Count > 0)
{
    return list[0];
}
Run Code Online (Sandbox Code Playgroud)

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

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

Jon*_*eet 18

怀疑它是"安全的","它不会导致任何灾难性的错误" - 但你可能会得到过时的数据.那是因为我怀疑它只是在一个简单的变量中存在,并且未来可能就是这种情况.但这与保证不一样.

就个人而言,我会保持简单:如果你正在访问共享的可变数据,那么只能在锁中使用(对同一数据使用相同的锁).如果你有适当的隔离,那么无锁编程就是非常好的(所以你知道你有适当的内存障碍,并且你知道在你从它读取时你永远不会在一个线程中修改它在另一个)但听起来不是这里的情况.

好消息是获得无可争议的锁是非常便宜的 - 所以如果我是你,我会选择安全的路线.线程很难在没有引入竞争条件的情况下进行,竞争条件可能没有显着的性能优势,但代价是罕见且不可重现的错误.

  • @Jon Skeet:伙计,我很幸运能让你为此付出代价!在这种情况下,陈旧值完全可以,因为它仅用于确定另一个集合的"粗略"初始大小.我负担不起的是Count属性返回负值或非常大的值.如果另一个线程正在修改集合的同时我拉出Count值,是否可能发生这种情况?Count是一个int,所以即使它被修改了,它也会以原子方式完成,对吧? (2认同)
  • @Matt:我*期望*这没问题-但我仍然会在我自己的代码中安全地玩它。您将依赖可能在以后版本中更改的实现细节。是的,它可能只是存储在一个简单的 `int` 变量中,但你真的想依赖它吗?您关心性能还是代码简单性?如果您使用 C# 3 并锁定集合本身,则可以创建一个扩展方法(“LockedCount()”或类似的方法)以使其更容易一些。 (2认同)