我有一个变量,我用来表示状态.它可以从多个线程读取和写入.
我正在使用Interlocked.Exchange并Interlocked.CompareExchange改变它.但是我从多个线程中读取它.
我知道volatile可以用来确保变量不在本地缓存,但总是直接从内存中读取.
但是,如果我将变量设置为volatile,则会生成有关使用volatile并使用ref传递给Interlocked方法的警告.
我想确保每个线程都读取变量的最新值而不是某些缓存版本,但我不能使用volatile.
有一个Interlocked.Read 但是它适用于64位类型,并且在紧凑的框架上不可用.它的文档说32位类型不需要它,因为它们已经在一次操作中执行.
如果您使用Interlocked方法进行所有访问,则互联网上有一些声明您不需要使用volatile.但是,您无法使用Interlocked方法读取32位变量,因此您无法使用Interlocked方法进行所有访问.
有没有办法在不使用锁的情况下完成我的变量的线程安全读写?
有人问过像我这样的问题,但我的情况有点不同.现在的问题是,"为什么volatile关键字不允许C#在类型System.Double和System.Int64等?"
乍一看,我回答了我的同事,"好吧,在32位机器上,这些类型至少需要两个滴答才能进入处理器,.Net框架的目的是抽象出像这样的处理器特定细节. " 他回答说:"如果由于处理器特定的问题导致你无法使用某个功能,那么它就不会抽象出任何东西!"
他暗示一个特定于处理器的细节不应该出现在使用框架的人身上,这个框架"抽象"远离程序员的细节.因此,框架(或C#)应该抽象出那些并做它需要做的事情来提供相同的保证System.Double等等(无论是信号量,内存障碍还是其他).我认为框架不应该增加信号量的开销volatile,因为程序员不期望使用这样的关键字这样的开销,因为32位类型不需要信号量.64位类型的更大开销可能会让人感到意外,因此,更好的.Net框架只是不允许它,并且如果开销是可接受的,那么就可以在更大的类型上使用自己的信号量.
这导致我们调查volatile关键字的全部内容.(见本页).该页面在说明中说明:
在C#中,在字段上使用volatile修饰符可确保对该字段的所有访问都使用VolatileRead或VolatileWrite.
嗯..... VolatileRead并且VolatileWrite都支持我们的64位类型!! 那么我的问题是,
"为什么volatile关键字不允许
C#在类型System.Double和System.Int64等?"
(这是重复:如何正确读取Interlocked.Increment'ed int字段?但是,在阅读了答案和评论之后,我仍然不确定正确的答案.)
有些代码我不拥有,也无法更改为使用在几个不同线程中增加int计数器(numberOfUpdates)的锁.所有通话都使用:
Interlocked.Increment(ref numberOfUpdates);
Run Code Online (Sandbox Code Playgroud)
我想在我的代码中读取numberOfUpdates.既然这是一个int,我知道它不会撕裂.但是,确保我获得最新价值的最佳方法是什么?看起来我的选择是:
int localNumberOfUpdates = Interlocked.CompareExchange(ref numberOfUpdates, 0, 0);
Run Code Online (Sandbox Code Playgroud)
要么
int localNumberOfUpdates = Thread.VolatileRead(numberOfUpdates);
Run Code Online (Sandbox Code Playgroud)
两者都有效(无论优化,重新排序,缓存等,都可以提供最新的价值)?一个比另一个更受欢迎吗?还有第三种选择更好吗?
Interlocked.Exchange和Volatile.Write有什么区别?
两种方法都更新某些变量的值.有人可以总结何时使用它们?
http://msdn.microsoft.com/ru-ru/library/bb337971 和http://msdn.microsoft.com/en-us/library/gg712713.aspx
特别是我需要更新我的数组的双项,我希望另一个线程看到最新的值.什么是首选?Interlocked.Exchange(ref arr[3], myValue)或Volatile.Write(ref arr[3], info);在那里arr被声明为double?
================================================== ==========================真实的例子,我声明了这样的双数组:
private double[] _cachedProduct;
Run Code Online (Sandbox Code Playgroud)
在一个线程中,我更新它:
_cachedProduct[instrumentId] = calcValue;
...
are.Set();
Run Code Online (Sandbox Code Playgroud)
在另一个线程中,我像这样读取这个数组:
while(true) {
are.WaitOne();
...
result += _cachedProduct[instrumentId];
...
}
Run Code Online (Sandbox Code Playgroud)
对我来说它只是工作正常.然而,为了确保"它将永远有效",无论它看起来我应该添加Volatile.Write或Interlocked.Exchange.因为双重更新不能保证是原子的http://msdn.microsoft.com/en-us/library/aa691278%28VS.71%29.aspx
在这个问题的答案中,我希望看到Volatile和Interlocked类的详细比较.为什么我们需要2节课?哪一个和何时使用?
假设我有一个变量"counter",并且有几个线程通过使用Interlocked访问和设置"counter"的值,即:
int value = Interlocked.Increment(ref counter);
Run Code Online (Sandbox Code Playgroud)
和
int value = Interlocked.Decrement(ref counter);
Run Code Online (Sandbox Code Playgroud)
我可以假设,Interlocked所做的更改将在所有线程中可见吗?
如果没有,我该怎么做才能使所有线程同步变量?
编辑:有人建议我使用volatile.但是当我将"计数器"设置为volatile时,会出现编译器警告"对volatile字段的引用不会被视为volatile".
当我阅读在线帮助时,它说:"通常不应使用ref或out参数传递易失性字段".
Read(Int64).NET系统类System.Threading.Volatile和的方法有什么区别System.Threading.Interlocked?
具体而言,关于(a)原子性和(b)内存排序,它们各自的保证/行为是什么。
请注意,这是关于Volatile阶级,不是的volatile(小写)的关键字。
MS docs状态:
易读方法
读取字段的值。在需要它的系统上,插入一个内存屏障,以防止处理器按以下方式重新排序内存操作:如果在代码中此方法之后出现读取或写入,则处理器无法在此方法之前移动它。
...
退货
Int64读取的值。不管处理器数量或处理器缓存状态如何,此值都是计算机中任何处理器最新写入的值。
与
Interlocked.Read(Int64)方法
返回作为原子操作加载的64位值。
似乎特别令人困惑的是,这些Volatile文档没有谈论原子性,并且这些Interlocked文档没有谈论排序/内存障碍。
旁注:作为参考:我更熟悉C ++原子API,其中原子操作也总是指定内存排序语义。
Pavel有用地提供的问题链接(和可传递链接)很好地解释了挥发性记忆屏障和原子无撕裂阅读的区别/正交性,但它们并没有解释这两个概念如何应用于这两个类别。
Volatile.Read保证原子性?Interlocked.Read(或者,真的,任何的Interlocked功能)做出的内存为了任何保证?