4

可能的重复:
易失性 vs. 互锁 vs. 锁定

我试图了解我想使用 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,那在功能上是否等效?(假设我不依赖于原子读/增量/写,比如增加一个值)。

4

2 回答 2

4

volatile保证访问对其他内核可见,Interlocked. 与 interlocked 的不同之处在于它使用完整的内存屏障来保证并处理非原子操作。Volatile 可能不使用完整的内存屏障(取决于平台,例如 x86/x64 不需要具有 volatile 的完整内存屏障......),但只会使原子操作“线程安全”。

通常建议避免使用 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.

于 2012-10-11T15:31:52.643 回答
2

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

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

于 2012-10-11T15:23:14.447 回答