注意:此问题已被更改为count
。对于当前的问题,而不是Thread.VolatileRead
我会用Interlocked.Read
,其中也有挥发性的语义,还将处理这里讨论和引入
原子的读是没有锁定保障问题的64位读的问题,因为读的正确对齐的32位或更小,这你count
是,保证是原子的值。
这与64位值不同,如果它从-1开始,并在另一个线程将其递增时读取,则可能导致读取值为-1(在增量之前发生),0(在增量之后发生)或者4294967295或-4294967296(32位写入0,其他32位等待写入)。
原子增量Interlocked.Increment
表示整个增量操作是原子的。考虑增量是概念上的:
- 阅读值。
- 添加一个值。
- 写入数值。
然后,如果x
是54和一个线程试图增加它而另一尝试将其设置为67,这两个正确可能的值是67(增量首先发生,然后被写入结束)或68(分配发生第一,然后递增),但非原子增量可能导致55(增量读取,分配67发生,递增写入)。
更常见的真实情况是x
是54,一个线程递增,另一个递减。这里唯一有效的结果是54(一个向上,然后一个向下,反之亦然),但如果不是原子的,那么可能的结果是53,54和55.
如果你只是想要一个以原子方式递增的计数,正确的代码是:
private int count;
public int Count
{
get
{
return Thread.VolatileRead(byref count);
}
}
public void Increment
{
Interlocked.Increment(count);
}
如果不管你想采取什么行动,那么它需要更强的锁定。这是因为使用计数的线程在其操作完成之前可能会过时。在这种情况下,您需要锁定所有关心计数以及改变计数的所有内容。究竟需要如何完成(以及它的重要性如何)取决于你的用例的更多事项,而不是从你的问题中推断出来。
编辑:哦,你可能想锁定只是为了强制内存屏障。如果要删除锁,您可能还想将Count
的实现更改为return Thread.VolatileRead(ref count);
以确保CPU缓存已刷新。这取决于在这种情况下缓存过期的重要性。 (另一种选择是使count
易失性,因为所有的读写操作都是不稳定的,请注意,Interlocked
操作不需要这些操作,因为它们总是不稳定的。)
编辑2:的确,你是如此可能想要这种不稳定的阅读,我正在改变上面的答案。它是可能你不会在意它提供什么,但不太可能。
切勿锁定'this',该锁将与类锁定对象之外的锁相冲突。只锁定私人引用类型的成员。如果你没有一个适当的私有引用类型的成员,而不是正确类型的静态或实例类型的成员,那么就应该为此目的设置'private object _myLock'''或'private static object _myStaticLock''。 – 2010-10-04 14:27:17
@Jon,注意和编辑 – 2010-10-04 14:30:13
我仍然有这样的感觉,你可能会有比这个问题更大的鱼来炒。就这个问题而言,我很满意我的答案,但它并没有处理线程A基于count进行某些操作的事情,而线程B更改了计数,因为这是否是一个问题,以及如何处理它如果是,取决于你比这里更多。 – 2010-10-04 14:37:52