在 C# 中进行线程同步时,我是否应该在读取值或只是更改它时锁定一个对象?
例如我有 Queue<T> 对象。我应该在执行 Enqueue 和 Dequeue 时锁定它,还是在检查 Count 等值时也应该锁定它?
在 C# 中进行线程同步时,我是否应该在读取值或只是更改它时锁定一个对象?
例如我有 Queue<T> 对象。我应该在执行 Enqueue 和 Dequeue 时锁定它,还是在检查 Count 等值时也应该锁定它?
来自 MSDN:
一个 Queue<(Of <(T>)>) 可以同时支持多个读取器,只要不修改集合即可。即便如此,通过集合枚举本质上不是线程安全的过程。为了保证枚举过程中的线程安全,可以在整个枚举过程中锁定集合。要允许集合被多个线程访问以进行读写,您必须实现自己的同步。
您应该确保在项目排队时没有阅读器处于活动状态(锁定可能是个好主意)。
查看反射器中的计数显示来自私有字段的读取。这可能没问题,具体取决于您对值的处理方式。这意味着你不应该做这样的事情(没有适当的锁定):
if(queue.Count > 0)
queue.Dequeue();
取决于你想用锁做什么。通常这种锁定需要读写器锁定机制。
读者/作者锁定意味着读者共享一个锁,因此您可以让多个读者同时读取集合,但要写入,您应该获取排他锁。
如果你不锁定它,你可能会得到一个较旧的值。可能会出现竞争条件,即执行写入操作会更改 Count,但您将获得更改之前的值。例如,如果队列只有一个项目,并且一个线程调用 dequeue,另一个线程可能会读取计数,发现它仍然是 1,然后再次调用 dequeue。在授予锁定之前不会完成第二次调用,但那时队列实际上是空的。
CLR 保证原子读取最大处理器宽度的值。因此,如果您在 32 位上运行,读取int
s 将是原子的。如果您在 64 位机器上运行,读取long
s 将是原子的。因此,如果Count
是,Int32
则无需锁定。
这篇文章与您的问题有关。