C#中的读写操作的易失性与互锁性

时间:2012-10-11 15:14:43

标签: c# multithreading .net-4.0 volatile

  

可能重复:
  Volatile vs. Interlocked vs. lock

我想了解当我想使用volatile关键字vs Interlocked时的情况。

如果我有一个变量,其中每个从该对象读取和写入的变量都是通过Interlocked.Exchange,那么将该变量标记为volatile是基本相同的吗?

private object _threadSafe;
private void Test()
{
    var tmp = new object();
    Interlocked.Exchange(ref tmp, _threadSafe); //read
    Interlocked.Exchange(ref _threadSafe, "hi"); //write

}

如果,而不是这个,_threadSafe对象被标记为volatile并且我删除了Interlocked,那么它在功能上是否相同? (假设我不依赖于原子读/递增/写入,比如递增一个值)。

2 个答案:

答案 0 :(得分:4)

volatile保证访问对其他核心可见,Interlocked也是如此。与互锁的区别在于它使用完整的内存屏障来保证并处理非原子操作。易失性可能不会使用完整的内存屏障(取决于平台,例如x86 / x64不需要带有volatile的完整内存屏障......)但只能使原子操作“线程安全”。

通常建议避免使用volatile,因为它会使每个 atomic 访问该变量“volatile”(这对x86 / x64来说可能不是那么大)并且有点隐藏了访问变量是不同的。通常更推荐类似Interlocked之类的东西,因为它明确详细说明了每次使用变量时的线程安全问题。

此外,您不能在局部变量上使用volatile,因此如果您想使用具有多个线程的局部变量,则可能需要Interlocked。例如:

static void Main()
{
  bool complete = false; 
  var t = new Thread (() =>
  {
    bool toggle = false;
    while (!complete) toggle = !toggle;
  });
  t.Start();
  Thread.Sleep (1000);
  complete = true;
  t.Join();        // Blocks indefinitely
}

更新:要明确,“访问”我指的是已经原子访问。显而易见,仅仅因为变量是“易变的”并不会使上的每一个操作都是线程安全的。那不是我说的。例如在某些平台上,尽管使用volatile,x ++仍然不是线程安全的。

答案 1 :(得分:2)

正如其他人所说,volatile不允许您执行Interlocked安全允许以原子方式完成的许多操作。尽管没有同步方法,但有些代码实际上是安全的,并且没有竞争条件。例如,如果有一个线程写入一个整数和N个不同的线程只读取,那么你可能没有任何问题,所有这些都不需要对该整数进行任何锁定。没有竞争条件,你检查变量,添加一个变量,然后让其他人设置它,然后你可以设置你的结果(因此导致你基本上覆盖他们的写)。

这里的问题是由于编译器优化,线程/处理器特定的缓存等,N个其他线程可能无法看到一个写入线程的更新。内存不会“同步”。他们每个人都会读/写完全不同的变量,因此其他线程不会看到变化。您需要添加特定的内存屏障,其中同步变量的所有各种表示都是同步的,以使代码起作用。当您使用lock或其他同步方法时,C#会自动知道需要有内存屏障,因此您无需告诉它任何内容。当使用无锁同步时,没有什么可以引入这个障碍,所以这就是volatile的用途。