Roy*_*mir 13 .net c# multithreading
我一直在阅读,以Full fences防止任何类型的指令重新排序或缓存围栏(通过memoryBarrier)
然后我读到了volatile 哪些会产生"半围栏":
volatile关键字指示编译器在每次从该字段读取时生成一个获取栅栏,并在每次写入该字段时生成一个释放栅栏.
acquire-fence
获取栅栏可防止其他读/写在栅栏前移动;
release-fence
释放栅栏可防止在栅栏后移动其他读/写.
有人可以用简单的英语向我解释这两句话吗?
(围栏在哪里?)
在这里得到一些答案后 - 我已经制作了一幅可以帮助每个人的图画 - 我想.
Bri*_*eon 13
你提到的措辞看起来像我经常使用的那样.规范说明了这一点:
但是,我通常会使用你在问题中引用的措辞,因为我想把重点放在可以移动指令的事实上.您引用的措辞和规范是等效的.
我将介绍几个例子.在这些例子中,我将使用一个特殊的符号,使用↑箭头表示释放栅栏,↓箭头表示获取栅栏.没有其他指令可以通过↑箭头向上浮动或超过↓箭头.把箭头想象成排斥它的一切.
请考虑以下代码.
static int x = 0;
static int y = 0;
static void Main()
{
x++
y++;
}
Run Code Online (Sandbox Code Playgroud)
重写它以显示各个指令看起来像这样.
static void Main()
{
read x into register1
increment register1
write register1 into x
read y into register1
increment register1
write register1 into y
}
Run Code Online (Sandbox Code Playgroud)
现在,因为在这个例子中没有内存障碍,只要执行线程所感知的逻辑序列与物理序列一致, C#编译器,JIT编译器或硬件就可以以许多不同的方式自由地优化它.这是一个这样的优化.注意如何读取/写入x和y交换.
static void Main()
{
read y into register1
read x into register2
increment register1
increment register2
write register1 into y
write register2 into x
}
Run Code Online (Sandbox Code Playgroud)
现在这次将这些变量改为volatile.我将使用箭头符号来标记记忆障碍.注意的顺序如何读取和写入/ x和y被保留.这是因为指令不能越过我们的障碍(用↓和↑箭头表示).现在,这很重要.请注意,x指令的增量和写入仍然允许向下浮动并且读取y浮动.这仍然有效,因为我们使用半围栏.
static volatile int x = 0;
static volatile int y = 0;
static void Main()
{
read x into register1
? // volatile read
read y into register2
? // volatile read
increment register1
increment register2
? // volatile write
write register1 into x
? // volatile write
write register2 into y
}
Run Code Online (Sandbox Code Playgroud)
这是一个非常简单的例子.看看我的答案,这是一个非常重要的例子,说明如何volatile在双重检查模式中发挥作用.我使用与此处使用的相同的箭头符号,以便于查看正在发生的事情.
现在,我们也有了使用的Thread.MemoryBarrier方法.它会产生一个完整的围栏.因此,如果我们使用箭头符号,我们可以想象它是如何工作的.
考虑这个例子.
static int x = 0;
static int y = 0;
static void Main
{
x++;
Thread.MemoryBarrier();
y++;
}
Run Code Online (Sandbox Code Playgroud)
如果我们要像以前一样显示各个指令,那么这看起来像这样.请注意,现在完全阻止了指令移动.在不损害指令的逻辑顺序的情况下,实际上没有其他方法可以执行.
static void Main()
{
read x into register1
increment register1
write register1 into x
? // Thread.MemoryBarrier
? // Thread.MemoryBarrier
read y into register1
increment register1
write register1 into y
}
Run Code Online (Sandbox Code Playgroud)
好的,还有一个例子.这次让我们使用VB.NET.VB.NET没有volatile关键字.那么我们如何模仿VB.NET中的易失性读取呢?我们会用Thread.MemoryBarrier.1
Public Function VolatileRead(ByRef address as Integer) as Integer
Dim local = address
Thread.MemoryBarrier()
Return local
End Function
Run Code Online (Sandbox Code Playgroud)
这就是我们用箭头符号看起来的样子.
Public Function VolatileRead(ByRef address as Integer) as Integer
read address into register1
? // Thread.MemoryBarrier
? // Thread.MemoryBarrier
return register1
End Function
Run Code Online (Sandbox Code Playgroud)
重要的是要注意,因为我们想要模仿易失性读取,所以Thread.MemoryBarrier必须在实际读取之后放置调用.不要陷入认为易失性读取意味着"新读"并且易失性写意味着"提交写入"的陷阱.这不是它的工作原理,它肯定不是规范所描述的.
更新:
参考图像.
等待!我证实所有的写作都已完成!
和
等待!我正在验证所有消费者都有现在的价值!
这是我正在谈论的陷阱.这些陈述并不完全准确.是的,在硬件级别实现的内存屏障可以同步缓存一致性线,因此上面的语句可能在某种程度上准确地说明了所发生的事情.但是,volatile只不过是限制指令的移动.该规范没有提到从内存中加载值或将内存存储到内存屏障位置的内存.
1 当然,Thread.VolatileRead内置已经存在.你会注意到它的实现与我在这里完全一样.
| 归档时间: |
|
| 查看次数: |
3286 次 |
| 最近记录: |