我有一个属性,它的类型是长(Int64)。
如果它是一个 int,那么我可以将支持字段声明为:
private volatile int _myInt;
并创建一个简单的获取和设置访问器。
但是,C# 编译器不允许将 volatile 关键字与 long 类型一起使用,即使使用 x64 项目设置也是如此。所以情况是,即使我们确定读/写操作在这个变量上是原子的,不幸的是,存在读取变量的线程将获取并使用旧(处理器或 CLR/JIT 优化器)缓存值的危险......
问题 1:这是否意味着我必须使用互锁来防止读取缓存值,而不是简单地在 get 访问器中读取此值?
get
{
return Interlocked.CompareExchange(ref _myLong, 0, 0);
}
这意味着相当多的开销...
问题 2:仍然假设一个有保证的 64 位架构,在 set 访问器中进行简单的赋值是否足够,例如:
set
{
_myLong = value;
}
提前致谢
您可以使用 System.Threading.Volatile 类来解决此问题。 例如:
class Example {
private long _prop;
public long prop {
get { return Volatile.Read(ref _prop); }
set { Volatile.Write(ref _prop, value); }
}
}
虽然这看起来效率低下,但实际上在 x64 处理器上生成了高效的代码。 抖动具有 Volatile 类的内置知识,并直接将其转换为机器代码,而不是依赖于框架实现。 x64抖动知道长在64位英特尔/AMD处理器上是原子的。 例如:
static void Main(string[] args) {
var obj = new Example();
obj.prop = 42;
Console.WriteLine(obj.prop);
}
生成此机器代码:
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
并注意属性获取者和二传手是如何被完全消除的,直接访问Example._prop字段。 这就是你要找的。 如果你曾经在具有弱内存模型的处理器(如 ARM)上运行此代码,那么它仍然可以正常工作,并按照此类处理器的要求生成适当的获取和释放语义。