相互联系,反复无常

我有一个用来表示状态的变量。可以从多个线程读写它。

我使用 Interlocked.ExchangeInterlocked.CompareExchange来改变它。但是我从多个线程读取它。

我知道 volatile可以用来确保变量不是本地缓存的,而是总是直接从内存中读取。

但是,如果我将变量设置为 volatile,那么它将生成一个关于使用 volatile 和使用 ref 传递给 Interlock 方法的警告。

我希望确保每个线程读取的是变量的最新值,而不是某个缓存版本,但是我不能使用 volic。

有一个 Interlocked.Read,但它适用于64位类型,并且在紧凑的框架中不可用。有关它的文档说,32位类型不需要它,因为它们已经在单个操作中执行。

互联网上有一些声明说,如果你使用联锁方法进行所有的访问,你就不需要易失性。但是,您不能使用 Interlock 方法读取32位变量,因此不可能对所有访问都使用 Interlock 方法。

有没有什么方法可以不使用 lock 实现线程安全的变量读写?

16525 次浏览

在使用 Interlocked.Xxx函数时,您可以安全地忽略这个警告(请参见 这个问题) ,因为它们总是执行易失性操作。因此,volatile变量对于共享状态来说是完全可以的。如果您想不惜一切代价消除警告,那么实际上 可以将与 Interlocked.CompareExchange (ref counter, 0, 0)进行连锁读取。

编辑: 实际上,如果要直接写入状态变量 只有(即不使用 Interlocked.Xxx) ,就需要 volatile正如 Jerryjvl 提到的,读取用互锁(或易失性)操作更新的变量将使用最新的值。

实际上不应该同时使用联锁操作和可变操作。你得到警告的原因是因为它(几乎?)总是表明你误解了你正在做的事情。

过分简化和解释:
volatile表示每个读操作都需要从内存中重新读取,因为可能有其他线程在更新变量。当应用到一个可以由您所运行的体系结构自动读/写的字段时,除非您使用 long/ulong,否则这应该是您所需要做的全部事情,其他大多数类型都可以自动读/写。

当一个字段没有被标记为易失性时,您可以使用 Interlocked操作来做出类似的保证,因为它会导致缓存被刷新,以便更新对于所有其他处理器都是可见的... ... 这样做的好处是,您将开销放在了更新上而不是读取上。

这两种方法中哪一种执行得最好,取决于您具体在做什么。这种解释过于简单化了。但从这一点可以清楚地看出,同时做这两件事是毫无意义的。