根据MSDN:
volatile关键字表示某个字段可能被同时执行的多个线程修改.声明为volatile的字段不受编译器优化的约束,这些优化假定由单个线程进行访问.这可确保始终在字段中显示最新值.
请注意最后一句:
这可确保始终在字段中显示最新值.
但是,此关键字存在问题.
我已经读过它可以改变指令的顺序:
Run Code Online (Sandbox Code Playgroud)First instruction Second instruction Can they be swapped? Read Read No Read Write No Write Write No Write Read Yes! <----
这意味着John为一个易变的字段设置了一个值,后来 Paul想要阅读该字段,Paul正在获得旧的价值!
这是怎么回事?这不是主要的工作吗?
我知道还有其他解决方案,但我的问题是关于volatile关键字.
我(作为程序员)是否需要阻止使用此关键字 - 因为这种奇怪的行为?
我一直在阅读,以Full fences防止任何类型的指令重新排序或缓存围栏(通过memoryBarrier)
然后我读到了volatile 哪些会产生"半围栏":
volatile关键字指示编译器在每次从该字段读取时生成一个获取栅栏,并在每次写入该字段时生成一个释放栅栏.
acquire-fence
获取栅栏可防止其他读/写在栅栏前移动;
release-fence
释放栅栏可防止在栅栏后移动其他读/写.
有人可以用简单的英语向我解释这两句话吗?
(围栏在哪里?)
在这里得到一些答案后 - 我已经制作了一幅可以帮助每个人的图画 - 我想.
我试图看看如何应用围栏.
我有这个代码(无限期地阻止):
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
}
Run Code Online (Sandbox Code Playgroud)
写作volatile bool _complete;解决了这个问题.
获取围栏:
获取栅栏可防止其他读/写在栅栏前移动;
但是如果我用箭头来说明它? (想想箭头就可以把所有东西推开.)
所以现在 - 代码看起来像:
var t = new Thread(() => {
bool toggle = false;
while( !complete )
??????? // instructions can't go up before this fence.
{
toggle = !toggle;
}
}); …Run Code Online (Sandbox Code Playgroud) 众所周知,与Java的易失性不同,.NET的版本允许使用来自另一个位置的以下易失性读取来重新排序易失性写入.当它是一个问题时 MemoryBarier,建议放在它们之间,或者Interlocked.Exchange可以用来代替volatile写.
它可以工作,但MemoryBarier在高度优化的无锁代码中使用时可能成为性能杀手.
我想了一下,想出了一个主意.我希望有人告诉我,我是否采取了正确的方式.
所以,这个想法如下:
我们希望防止这两种访问之间的重新排序:
volatile1 write
volatile2 read
Run Code Online (Sandbox Code Playgroud)
从.NET MM我们知道:
1) writes to a variable cannot be reordered with a following read from
the same variable
2) no volatile accesses can be eliminated
3) no memory accesses can be reordered with a previous volatile read
Run Code Online (Sandbox Code Playgroud)
为了防止写入和读取之间不必要的重新排序,我们从刚刚写入的变量中引入了一个虚拟易失性读取:
A) volatile1 write
B) volatile1 read [to a visible (accessible | potentially shared) location]
C) volatile2 read
Run Code Online (Sandbox Code Playgroud)
在这种情况下,B不能用A重新排序,因为它们都访问相同的变量, C不能用B重新排序,因为两个易失性读取不能相互重新排序,并且传递C …
fallowing子句来自jetbrains.net在阅读了这篇以及网上的其他文章后,我仍然不明白在第一个线程进入锁之后如何返回null.有人确实理解它可以帮助我并以更人性化的方式解释它吗?
"考虑以下代码:
public class Foo
{
private static Foo instance;
private static readonly object padlock = new object();
public static Foo Get()
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Foo();
}
}
}
return instance;
}
};
Run Code Online (Sandbox Code Playgroud)
给定上面的代码,初始化Foo实例的写入可以被延迟,直到写入实例值,从而产生实例返回处于单元化状态的对象的可能性.
为了避免这种情况,必须使实例值易变."