9

我知道在 java 中,如果您有多个线程访问未标记为 volatile 的变量,则可能会出现一些意外行为。

例子:

private boolean bExit;

 while(!bExit) {
    checkUserPosition();
    updateUserPosition();
 }

如果将 bExit 变量标记为 voilatile,则可以保证其他线程将看到最新的值。

c# 的行为方式是否相同?

更新

例如,在 C# 中,如果您这样做:

int counter = ...;

for(...)
{
   new Thread(delegate()
   {

      Interlocked.Decrement(ref counter);
   }
}


if(counter == 0) 
{
   // halt program
}

在上面,在 c# 中,您是否必须将计数器变量标记为 volatile 或者这将按预期工作?

4

2 回答 2

0

您需要小心一点,因为您在示例中选择了两种不同的类型。我的一般经验法则是 volatile 最适合与布尔值一起使用,并且您可能需要其他类型的同步机制。在 C# 和 java 中使用 volatile 布尔值来停止循环或任务执行是很常见的,它可以从多个线程中看到:

class Processor
{
    private volatile Boolean _stopProcessing = false;

    public void process()
    {
        do
        { ... }
        while (!_stopProcessing);
    }

    public void cancel()
    {
        _stopProcessing = true;
    }
}

以上将在 C# 或 java 中工作,在此示例中,volatile 关键字意味着 process 方法中的读取将来自实际的当前值,而不是缓存值。编译器或 VM 可能会选择以其他方式允许缓存该值,因为进程中的循环不会更改 _stopProcessing 本身。

所以你的第一个问题的答案是肯定的。

虽然您可以在 int 上使用 volatile ,但这仅在您只读取或写入单个值时才有帮助。一旦你做任何更复杂的事情,例如递增,你就需要一些其他类型的同步,比如你对 Interlocked 的使用。但是,在您的第二个示例中,您仍在读取计数器的值而没有任何同步,因此您有效地依赖于同步的计数器的其他用法(例如,使用互锁)。

在您的第二个示例中,您仍然最好将您的 int 标记为 volatile,这样您就可以再次确保您获得的是当前值而不是某个缓存版本。但是,仅使用 volatile 是不够的,因为底部的读取可能与标准减量(计数器--)重叠,而不是您正确使用 Interlocked。

于 2014-10-07T08:45:24.157 回答
0

如果将 bExit 变量标记为 voilatile,则可以保证其他线程将看到最新的值。c# 的行为方式是否相同?


是的,但这一切都取决于您如何访问共享变量。作为您的示例,如果您以线程安全的方式访问变量,Interlocked.Decrement即使不使用 voilatile 也不会成为问题。

volatile(来自 MSDN 的 C# 参考)

volatile 关键字表示一个字段可能被同时执行的多个线程修改。声明为 volatile 的字段不受假设由单个线程访问的编译器优化的影响。这确保了字段中始终存在最新的值

“volatile”关键字是做什么用的?

于 2013-05-01T20:18:37.993 回答