挥发性围栏演示?挥发性、围栏、演示

2023-09-02 01:59:29 作者:酒酿樱桃子

我试着去看看栅栏被应用。的

我有这个code(该块无限期):

I have this code (which Blocks indefinitely):

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
}

写作挥发性布尔_complete; 解决问题

采集围栏:

这是获取围栏prevents其他的读/写距离移动 栅栏前;

An acquire-fence prevents other reads/writes from being moved before the fence;

但是,如果我说明了使用箭头它的(想想箭头为推动所有一切的。)的

But if I illustrate it using an arrow ( Think of the arrowhead as pushing everything away.)

所以现在 - 在code可以是这样的:

so now - the code can look like :

 var t = new Thread(() => {
            bool toggle = false;
            while( !complete ) 
                    ↓↓↓↓↓↓↓     // instructions can't go up before this fence.  
               {
                 toggle = !toggle;
                }
        });

我不明白是怎么绘制出重present解决此问题的解决方案。

I don't understand how the illustrated drawing represent a solution for solving this issue.

我知道而(!完整)现在读取的真正价值。但它是如何与完整=真; 位置栅栏

I do know that while(!complete) now reads the real value. but how is it related to complete = true; location to the fence ?

推荐答案

制作完整挥发性做两件事情:

Making complete volatile does two things:

据prevents C#编译器或作出优化的抖动,将缓存的值完整

它引入了一个栅栏告诉缓存等的优化处理器读取和涉及要么pre取写入读或延迟写需要进行去优化,以确保一致性。

It introduces a fence that tells the processor that caching optimizations of other reads and writes that involve either pre-fetching reads or delaying writes need to be de-optimized to ensure consistency.

让我们考虑的第一位。抖动是完全有权利看到循环体:

Let's consider the first. The jitter is perfectly within its rights to see that the body of the loop:

    while(!complete) toggle = !toggle;

不修改完整,因此任何值完整具有在循环的开始是价值在于它将有永远。因此,抖动允许生成code,就像你写

does not modify complete and therefore whatever value complete has at the beginning of the loop is the value that it is going to have forever. So the jitter is allowed to generate code as though you'd written

    if (!complete) while(true) toggle = !toggle;

,或者更可能:

or, more likely:

    bool local = complete; 
    while(local) toggle = !toggle;

制作完整挥发性prevents既优化。

Making complete volatile prevents both optimizations.

但你正在寻找的是挥发性的第二个影响。假设你的两个线程在不同的处理器上运行。每个人都有其自己的处理器的高速缓存,这是主存储器的副本。让我们假设两个处理器取得了主内存的一个副本,其中完整是假的。当一个处理器的缓存设置完整来真的,如果完整不挥发那么切换处理器不需要注意到这一事实;它有它自己的缓存,其中完整仍是假的,这将是昂贵的,每次回到主内存。

But what you are looking for is the second effect of volatile. Suppose your two threads are running on different processors. Each has its own processor cache, which is a copy of main memory. Let's suppose that both processors have made a copy of main memory in which complete is false. When one processor's cache sets complete to true, if complete is not volatile then the "toggling" processor is not required to notice that fact; it has its own cache in which complete is still false and it would be expensive to go back to main memory every time.

标记完整挥发消除了这种优化。它是如何消除它是处理器的实现细节。也许在每次性写的写入被写入主存储器和所有其他处理器丢弃其缓存。或者,也许还有一些其他的策略。如何在处理器选择,使之发生是由制造商。

Marking complete as volatile eliminates this optimization. How it eliminates it is an implementation detail of the processor. Perhaps on every volatile write the write gets written to main memory and every other processor discards their cache. Or perhaps there is some other strategy. How the processors choose to make it happen is up to the manufacturer.

的一点是,任何时候你犯了一个场动荡,然后读或写出来,你是大规模破坏的编译器,抖动和处理器来优化您的code的能力。尽量不使用摆在首位挥发性领域;使用更高级别的结构,也不要线程之间共享内存。

The point is that any time you make a field volatile and then read or write it, you are massively disrupting the ability of the compiler, the jitter and the processor to optimize your code. Try to not use volatile fields in the first place; use higher-level constructs, and don't share memory between threads.

我试图以可视化的一句话:一个获取栅栏prevents其他的读/写从栅栏前,被感动......应该不会有什么指令栅栏前

I'm trying to visualize the sentence :"An acquire-fence prevents other reads/writes from being moved before the fence..." What instruction should not be before that fence ?

有关指导思想是可能会起到反作用。而不是想着一堆的说明的把心思放在序列读取和写入。其他的都是无关紧要的。

Thinking about instructions is probably counterproductive. Rather than thinking about a bunch of instructions just concentrate on the sequence of reads and writes. Everything else is irrelevant.

假设你有一个内存块,并且它的一部分被复制到两个高速缓存。出于性能考虑,你阅读和写作主要是为了缓存。每一个现在,然后你再同步与主内存缓存。什么影响这对一个序列读取和写入操作?

Suppose you have a block of memory, and part of it is copied to two caches. For performance reasons, you read and write mostly to the caches. Every now and then you re-synchronize the caches with main memory. What effect does this have on a sequence of reads and writes?

假设我们希望这发生在一个单一的整型变量:

Suppose we want this to happen to a single integer variable:

处理器的Alpha 0写入到主内存。 处理器布拉沃从主内存中读取0。 处理器布拉沃写1到主内存。 处理器的Alpha从主内存中读取1。

假设真正发生的事情是这样的:

Suppose what really happens is this:

处理器的Alpha 0写入到缓存中,并同步到主内存。 处理器布拉沃从主内存中缓存同步并读取0。 处理器布拉沃1写入缓存和同步缓存到主存。 处理器的Alpha读取0 - 一个陈旧的价值 - 从它的缓存。

时,究竟发生了什么以任何方式如何与此不同?

How is what really happened in any way different from this?

处理器的Alpha 0写入到主内存。 处理器布拉沃从主内存中读取0。 处理器的Alpha从主内存中读取0。 处理器布拉沃写1到主内存。

有没有什么不同。缓存变为写读写读到写读读写。它移动的所述一个读取时间向后,和,在这种情况下等同地,向前时刻移动写入之一

It isn't different. Caching turns "write read write read" into "write read read write". It moves one of the reads backwards in time, and, in this case equivalently, moves one of the writes forwards in time.

这个例子仅仅包括两个读和两个写在一个地方,但你能想象这样一个场景:有很多的读取和写入许多许多位置。该处理器具有广泛的lattitude移动时间向后读取和移动时间写前锋。在precise规则,什么动作是合法的,哪些不是从处理器不同的处理器。

This example just involves two reads and two writes to one location, but you can imagine a scenario where there are many reads and many writes to many locations. The processor has wide lattitude to move reads backwards in time and move writes forwards in time. The precise rules for what moves are legal and which are not differ from processor to processor.

A 围栏的是,prevents向后移动读取或向前移动过去,它写了一个障碍。所以,如果我们有:

A fence is a barrier that prevents reads from moving backwards or writes from moving forwards past it. So if we had:

处理器的Alpha 0写入到主内存。 处理器布拉沃从主内存中读取0。 处理器布拉沃写1到主内存。栅栏这里。 处理器的Alpha从主内存中读取1。

没有物质的处理器采用什么缓存策略,它现在不允许移动读4到栅栏前的任何点。同样它不允许在时间栅栏移动后写3提前任意点。如何处理器实现了围栏到它。

No matter what caching strategy a processor uses, it is now not allowed to move read 4 to any point before the fence. Similarly it is not allowed to move write 3 ahead in time to any point after the fence. How a processor implements a fence is up to it.