在没有锁定的保证64位体系结构上实现long属性的线程安全原子访问器的最佳方法

g.p*_*dou 1 .net c# multithreading

我有一个属性类型很长的属性(Int64).

如果它是一个int,那么我可以将支持字段声明为:

private volatile int _myInt;
Run Code Online (Sandbox Code Playgroud)

并创建一个简单的get和set访问器.

但是,即使使用x64项目设置,C#编译器也不允许使用类型为long的volatile关键字.所以情况是,即使我们确定读/写操作在这个变量上都是原子的,不幸的是,读取变量的线程有可能获得并使用旧的方式(处理器或CLR/JIT优化器)缓存值...

问题1:这是否意味着我不必简单地在get访问器中读取此值,而是使用Interlocked来防止读取缓存值?

get
{
    return Interlocked.CompareExchange(ref _myLong, 0, 0);
}
Run Code Online (Sandbox Code Playgroud)

这意味着一个相当开销......

问题2:仍然假设有保证的64位架构,在集合访问器中是否足够简单,如:

set
{
    _myLong = value;
}
Run Code Online (Sandbox Code Playgroud)

提前致谢

Han*_*ant 5

您可以使用System.Threading.Volatile类来解决此问题.例如:

class Example {
    private long _prop;
    public long prop {
        get { return Volatile.Read(ref _prop); }
        set { Volatile.Write(ref _prop, value); }
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然这看起来效率低下,但事实上它在x64处理器上生成了高效的代码.抖动具有Volatile类的内置知识,并直接将其转换为机器代码,而不是依赖于框架实现.x64抖动知道64位Intel/AMD处理器上的长度是原子的.例如:

    static void Main(string[] args) {
        var obj = new Example();
        obj.prop = 42;
        Console.WriteLine(obj.prop);
    }
Run Code Online (Sandbox Code Playgroud)

生成此机器代码:

00007FFA3DF43AB0  sub         rsp,28h                     ; setup stack frame
00007FFA3DF43AB4  lea         rcx,[7FFA3DF959B0h]         ; obj = new Example
00007FFA3DF43ABB  call        00007FFA9D5A2300  
00007FFA3DF43AC0  mov         qword ptr [rax+8],2Ah       ; obj.prop setter
00007FFA3DF43AC8  mov         rcx,qword ptr [rax+8]       ; obj.prop getter
00007FFA3DF43ACC  call        00007FFA9CD0CFD0            ; Console.WriteLine
00007FFA3DF43AD1  nop                                     ; alignment
00007FFA3DF43AD2  add         rsp,28h                     ; destroy stack frame
00007FFA3DF43AD6  ret                                     ; done
Run Code Online (Sandbox Code Playgroud)

并注意如何完全消除属性getter和setter,直接访问Example._prop字段.这就是你要找的东西.如果您曾在具有弱内存模型的处理器(如ARM)上运行此代码,那么它仍将正常工作,并根据此类处理器的要求生成相应的获取和释放语义.