0

根据这篇文章, http: //msdn.microsoft.com/en-us/magazine/cc163715.aspx,这是自旋锁类的实现:

class SpinLock
        {
            volatile int isEntered;
            // non-zero if the lock is entered 
            public void Enter()
            {
                while (Interlocked.CompareExchange(ref isEntered, 1, 0) != 0)
                {
                    Thread.Sleep(0); // force a thread context switch 
                }
            }
            public void Exit()
            {
                isEntered = 0;
            }
        }

我知道 volatile 的含义和作用,但我不明白为什么它在这里。

我想在另一个主题中问的最后一件事-读取对象的属性是否算作原子操作?据我了解,这里有两个读取:首先是对象引用,其次是属性读取。

4

2 回答 2

1

在(重新)研究了几天的内存模型之后,我对volatile在 SpinLock 中的使用有同样令人烦恼的问题。我找不到任何有问题的内存访问重新排序需要易失性访问所需要的任何一个栅栏。

  • 获得围栏:由 提供Interlocked.CompareExchange()
  • 释放栅栏:为了获得锁语义,我们希望“锁下”代码的所有副作用在锁被释放时都可见。但是 .NET 内存模型不允许对写入进行重新排序。因此,来自锁下的写入必须在不迟于清除isEntered.

更新
在基于 Core CLR 讨论的更多研究之后,我找出了断开连接的位置。首先,状态写入的两个流行来源还包括发布围栏,或者只是它们无法重新排序:

事实证明,“.NET Framework 2 模型”并没有被框架的所有更高版本和端口继承。最有启发性的文章最终是这篇文章:C# - The C# Memory Model in Theory and Practice, Part 2。作者解释了 IA64 和 ARM 的更宽松的 CLR 实现。本质上,正常的写入可以重新排序,因此在某些情况下需要内存栅栏。(他们如何决定在正常写入堆上的引用类型字段之前插入释放栅栏,这让我觉得这是为了保持与为 x86 编写的代码的兼容性而绝望的 hack,但在历史背景和流行的无锁对象发布模式。)

结论volatile在通用“.NET”实现中需要 SpinLock 字段,以确保在锁被释放之前发生锁下的写入——而不依赖于更强大的“.NET Framework 2 模型”或 CPU 架构。

于 2022-02-21T06:44:58.130 回答
0

在阅读了一些资料后,我的印象是 volatile 只会影响编译器/运行时在优化期间完成的重新排序。C# 5.0 Reference 表明它不止于此:

这些优化可以由编译器、运行时系统或硬件执行

然后我看到了这篇小文章 http://www.codeproject.com/Articles/31283/Volatile-fields-in-NET-A-look-inside

于 2016-02-15T22:32:03.570 回答