如何安全正确用Interlocked函数/TInterlocked原子写入无符号变量?A/B是否线程安全?
原子化写入无符号变量的正确方式及代码分析
一、安全原子化写入无符号变量的方法
在Delphi里用Interlocked系列函数或TInterlocked类做无符号变量的原子写入,关键得让原子函数直接操作目标变量的内存地址——原子操作是靠硬件级内存访问控制实现的,必须直接碰变量的内存空间才行。
具体到UInt64类型的变量:
- 可以把变量的地址强制转成
PInt64(或者PUInt64,部分Delphi版本支持),传给InterlockedExchange64或TInterlocked.Exchange; - 绝对不能传变量的临时值拷贝,必须传内存引用或指针。
二、代码里(A)(B)(C)(D)的实际效果
(A)和(B)完全无效,无原子性可言
TInterlocked.Exchange(Int64(tLastReload),1); //A InterlockedExchange64(Int64(tLastReload),2); //B
这两行代码连原变量tLastReload都改不了,更别说原子性和线程安全了:
Int64(tLastReload)是把变量的值转成Int64临时值,传递给原子函数的是这个临时值的内存(栈上的临时空间),和原变量tLastReload没关系;- 原子函数操作的是临时值,原变量纹丝不动,自然不存在任何线程安全保障。
(C)和(D)是正确的原子操作写法
TInterlocked.Exchange(PInt64(@tLastReload)^,3); //C InterlockedExchange64(PInt64(@tLastReload)^,4); //D
这两行是安全的,具备完整的原子性和线程安全性:
@tLastReload拿到原变量的内存地址,PInt64(@tLastReload)^解引用后直接指向原变量的内存;UInt64和Int64都是8字节连续存储,内存布局完全一致,强制转指针不会有问题;- 原子函数直接操作原变量的内存,硬件层面保证了写入操作不会被线程打断,不会出现读写冲突。
三、是否必须用(C)或(D)的写法
是的,必须用这类写法。只有让原子函数直接访问目标变量的内存地址,才能触发硬件级的原子操作。像(A)(B)那样传临时值的做法完全是无效操作,既改不了目标变量,也没有任何线程安全作用。
内容的提问来源于stack exchange,提问作者vincent
相关产品推荐
相关产品推荐

