我是一个爱折腾的人 - 毫无疑问。因此(除此之外很少),最近我进行了一个小实验,以确认我的怀疑:写入struct
不是原子操作,这意味着试图强制执行某些约束条件的所谓“不可变”值类型可能在其目标上假设失败。
我撰写了一篇博客文章,其中使用以下类型作为示例:
struct SolidStruct
{
public SolidStruct(int value)
{
X = Y = Z = value;
}
public readonly int X;
public readonly int Y;
public readonly int Z;
}
尽管上述代码看起来似乎永远不可能出现 X != Y
或 Y != Z
,但实际上,如果一个值正在被“中途赋值”,同时又被另一个线程复制到另一个位置,这种情况就会发生。
好的,这只是一个奇思妙想且没有多大意义。但我有一个想法:我的64位CPU应该能够原子地复制64位,对吗?那么如果我摆脱了Z
,只用X
和Y
呢?那只有64位;应该可以一步就覆盖它们。
果然,成功了。(我知道你们中的一些人可能正在皱眉头,想着,“是啊,当然。这怎么会有趣呢?”让我自high娱一下。)当然,我不知道这是否得到了保证,考虑到我的系统。我对寄存器、缓存未命中等几乎一无所知。 (我只是背诵听到过的术语而已);所以这一切对我来说都是黑匣子。
接下来,我尝试了一下——仍然只是出于直觉——一个由2个short
字段组成的32位结构体。这似乎也表现出了“原子可赋值”的特性。但是,然后我尝试用3个byte
字段来创建一个24位结构:结果不行。
突然间,结构体又出现了“中途赋值”复制的问题。
用2个byte
字段降到16位:又可原子化了!
有人能解释一下这是为什么吗?我听说过“位打包”,“缓存线跨越”,“对齐”等,但我不知道所有这些术语的含义,也不知道它们在这里是否相关。但是,我感觉有一个模式,只是说不清楚是什么;如果有人能解释一下,那就太好了。