Ste*_*end 7 .net c# multithreading
将interlocked操作与lock()(和其他更高级别的锁)混合时,是否保证原子读取?
我喜欢混合这样的锁定机制时的一般行为,以及Int32和Int64之间的任何差异.
private Int64 count;
private object _myLock;
public Int64 Count
{
get
{
lock(_myLock)
{
return count;
}
}
}
public void Increment
{
Interlocked.Increment(ref count);
}
Run Code Online (Sandbox Code Playgroud)
注意:在问题变为长期之前,已给出了这个答案(包括两个编辑)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的分配,增量写入).
更常见的实际情况是x54和一个线程递增而另一个递减.这里唯一有效的结果是54(一个上升,然后下一个,反之亦然),但如果不是原子的,那么可能的结果是53,54和55.
如果你只想要一个原子递增的计数,那么正确的代码是:
private int count;
public int Count
{
get
{
return Thread.VolatileRead(byref count);
}
}
public void Increment
{
Interlocked.Increment(count);
}
Run Code Online (Sandbox Code Playgroud)
但是,如果你想对这个数量采取行动,那么它将需要更强的锁定.这是因为使用计数的线程在操作完成之前可能会过时.在这种情况下,您需要锁定关注计数的所有内容以及更改它的所有内容.这个需要如何完成(以及它的重要性是什么)取决于您的用例的更多问题,而不是从您的问题中推断出来.
编辑:哦,你可能只想锁定以强制内存屏障.您可能还希望将Count实现更改return Thread.VolatileRead(ref count);为确保在要删除锁定时刷新CPU缓存.这取决于在这种情况下缓存过时的重要性.(另一种方法是生成countvolatile,因为所有的读写操作都是不稳定的.请注意,Interlocked操作不需要这些操作,因为它们总是不稳定的.)
编辑2:事实上,你很可能想要这个易变的读物,我正在改变上面的答案.这是可能的,你不会在意它所提供的,但不太可能.