属性有波动效应吗?

时间:2012-05-29 09:22:03

标签: c# multithreading concurrency

在下面的代码中,read1总是等于read2,前提是可以从其他线程更改属性Flag吗?这里关注的是Flag可能会被内联。

private bool Flag {get; set;}

public void MultithreadedMethod()
{
    var read1 = Flag;

    /* some more code */

    var read2 = Flag;    
}

UPD :某些其他线程可能会在Flag执行期间更改/* some more code */的值。在这种情况下,read1应与read2不同。它总是总是吗?不会内联将属性转换为非易失性字段,导致read1等于read2,尽管事实上Flag在读取之间发生了变化?

6 个答案:

答案 0 :(得分:15)

不,该属性不是volatile

虽然我无法为您的初始场景获得满意的演示,但这种替代方法应该很好地证明了这一说法:

class Program
{
    public bool Flag { get; set; }

    public void VolatilityTest()
    {
        bool work = false;
        while (!Flag)
        {
            work = !work; // fake work simulation
        }
    }

    static void Main(string[] args)
    {
        Program p = new Program();
        var t = new Thread(p.VolatilityTest);
        t.Start();
        Thread.Sleep(1000);
        p.Flag = true;
        t.Join();
    }
}

在Release模式下构建它会使程序死锁,从而证明Flag没有易失性行为(即它在读取之间得到“优化”)。

public bool Flag { get; set; }替换public volatile bool Flag;将使程序正确终止。

答案 1 :(得分:3)

是的,它可以自然地改变。

即使在提供的代码中,也不保证read1等于read2

考虑到同时/* some more code */已执行,Flag可能会受到其他线程的影响。

编辑

read1read2的相等与内联无关,Flagbool,因此它是值类型。所以

  • var read1 = Flag; //让我们说read1 TRUE
  • Flag = False
  • var read2 = Flag; // read2为FALSE,但read1保持为TRUE

这在多线程环境中也有效,导致您在值类型上运行。

如果这不是您所要求的,请澄清。

答案 2 :(得分:1)

如果可以从其他线程更改Flag,则无法保证read1和read2相同。您必须使用围绕代码的监视器/互斥锁,并确保Flag setter也尊重该互斥锁。

答案 3 :(得分:1)

总结其他回复,在这种情况下,无法预测执行代码后两个变量的值会发生什么。两者都是因为CLR和编译器对我们来说基本上是黑盒子,并且因为任何关于竞争条件结果的预测实际上都是赌博,在某些时候肯定是错误的。

无论如何,您无法在多线程环境中编写此类代码。

答案 4 :(得分:1)

autoproperty缺乏动力是令人失望的。我发现当使用带有[StructLayout(LayoutKind.Sequential,Pack = 4)]和Marshal.PtrToStructure的结构时,如果使用autoproperty,则不会按预期保留字节布局。我所做的是使用私有支持字段并将属性放在最后。

答案 5 :(得分:-1)

根据documentation

  

volatile关键字表示可以在中修改字段   通过诸如操作系统,硬件或a之类的程序来编程   并发执行线程。   ...   使用volatile修饰符可确保一个线程检索最多   另一个线程写的最新值。