在下面的代码中,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
在读取之间发生了变化?
答案 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
可能会受到其他线程的影响。
编辑
read1
和read2
的相等与内联无关,Flag
是bool
,因此它是值类型。所以
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)
volatile关键字表示可以在中修改字段 通过诸如操作系统,硬件或a之类的程序来编程 并发执行线程。 ... 使用volatile修饰符可确保一个线程检索最多 另一个线程写的最新值。