Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vue.js/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Java和C中易失性语义背后的原因是什么#_C#_Java_Multithreading_Multiprocessing_Volatile - Fatal编程技术网

C# Java和C中易失性语义背后的原因是什么#

C# Java和C中易失性语义背后的原因是什么#,c#,java,multithreading,multiprocessing,volatile,C#,Java,Multithreading,Multiprocessing,Volatile,C#和Java都定义了这一点 *易失性读取具有获取语义 *易失性写入具有发布语义 我的问题是: 这是定义volatile的唯一正确方法吗 如果没有,如果语义颠倒,情况会有很大的不同吗,即 volatile读取具有释放语义 易失性写入具有获取语义 acquire/release语义的威力不在于其他线程多久能看到volatile字段本身新写入的值,而在于volatile操作在不同线程之间建立“先发生后发生”关系的方式。如果线程a读取一个易失性字段,并在另一个线程B中看到一个写入该字段的值,那

C#和Java都定义了这一点
*易失性读取具有获取语义
*易失性写入具有发布语义

我的问题是:

  • 这是定义volatile的唯一正确方法吗
  • 如果没有,如果语义颠倒,情况会有很大的不同吗,即
    • volatile读取具有释放语义
    • 易失性写入具有获取语义

  • acquire/release语义的威力不在于其他线程多久能看到volatile字段本身新写入的值,而在于volatile操作在不同线程之间建立“先发生后发生”关系的方式。如果线程a读取一个易失性字段,并在另一个线程B中看到一个写入该字段的值,那么线程a也保证在执行易失性写入之前看到线程B写入其他(不一定是易失性)变量的值。这看起来像缓存刷新,但仅从读取volatile的线程的角度来看,其他不接触volatile字段的线程没有关于B的排序保证,并且可能会看到其早期的一些非volatile写入,但如果编译器/JIT如此倾向,则不会看到其他线程


    监视器获取/释放的特征类似于它们的诱导发生在关系之前-一个线程在监视器释放之前的操作保证在另一个线程后续获取同一监视器之后可见。volatile为您提供了与监视器同步相同的排序保证,但没有阻塞。

    volatile语义背后的推理源于,它是根据操作指定的:

    • 读取和写入变量
    • 监视器的锁定和解锁
    • 用螺纹开始和连接
    Java内存模型为Java程序中可能发生的操作定义了一个名为“before
    的函数。通常不能保证线程可以看到彼此操作的结果

    假设您有两个动作AB。为了保证执行动作B的线程可以看到动作a的结果,在a和B之间的关系之前必须有一个发生。如果没有,JVM可以自由地重新排序

    未正确同步的程序可能存在数据争用。当一个变量被>1个线程读取,而被>=1个线程写入,但读写操作没有通过“在排序之前发生”排序时,就会发生数据争用

    因此,一个正确同步的程序没有数据竞争,并且程序中的所有操作都以固定的顺序发生

    因此,行动通常只有部分有序,但在以下各项之间也有总有序:

    • 锁的获取和释放
    • 读取和写入volatile变量
    这些行动是完全有序的

    这使得用“后续”锁获取和易变变量读取来描述之前发生的情况变得合理

    关于你的问题:

  • 对于“发生在之前”关系,您可以使用另一种定义
    volatile
  • 按照上述定义,颠倒顺序是没有意义的,尤其是因为涉及到总顺序
  • 这说明了当两个线程使用公共的锁进行同步时,会发生before关系。线程A中的所有操作都是按程序顺序规则排序的,线程B中的操作也是如此。由于A释放锁M,而B随后获得锁M,因此,在释放锁之前A中的所有操作都是在获得锁之后B中的操作之前排序的。当两个线程在不同的锁上同步时,我们不能对它们之间的操作顺序说什么。两个线程中的操作之间没有“先发生后发生”的关系


    来源:

    Java在哪里定义这个?我以前从未听说过它。对于主要根据虚拟机定义的语言,像CPU缓存这样特定的东西会觉得不合适。有趣的文档:Doug Lea(Java concurrent team),正如@sarnold所暗示的,您正在将语义需求与实现细节混为一谈。碰巧CPU缓存可能需要刷新以满足易失性读取(虽然不一定如此),但这只是如何强制执行易失性读取定义的一个细节。CPU缓存最重要的架构是Alpha。在所有使用多个CPU/内核的现代幸存体系结构上,CPU缓存在硬件上使用了一些不同的CPU。因此,刷新或刷新CPU缓存只会降低性能,不会作为同步机制使用。这是volatile的获取和释放语义的一个很好的解释。但为什么线程A也必须看到线程B的所有非易失性写操作呢。提供了一个很好的解释。基本上,它使volatile更加有用,因为它们实际上适用于设置volatile标志以表示特定操作已完成的情况——另一个读取该标志的线程可以知道该操作已完成,而不可能看到某些部分完成的中间状态;我慢慢地开始学习更多关于记忆模型的知识,这有助于巩固我的理解。