在一个变量中存储 2 个值
Store 2 values in one variable
这段 Go 代码是否正确且可移植,我需要在一个变量中存储 2 个计数器(每次调用只会更新一个计数器)以避免在我将要使用单个 atomic.AddUint64() 的实际代码中锁定而不是锁定整个结构。
package main
import "fmt"
var long uint64 // Actual counters storage
func main() {
left := uint32(100) // First counter
right := uint32(200) // Second counter
long = uint64(left)
long = long << 32 | uint64(right)
fmt.Println(left, right)
long += uint64(1 << 32) // Increment left
long += 1 // Increment right
xLeft := uint32(long >> 32) // Get left
xRight := uint32(long) // Get right
fmt.Println(xLeft, xRight)
}
Is this Go code correct and portable
这是正确的,只要您使用 64 位宽度的无符号整数。
在这种情况下,可移植性由 sync/atomic
包提供给 Go 编译器支持的体系结构。但是请注意,并非所有体系结构都支持对 64 位宽数据进行 "true" 原子操作。例如,the i386 implementation 使用 CAS 循环:
TEXT ·AddUint64(SB),NOSPLIT,[=10=]-20
// no XADDQ so use CMPXCHG8B loop
MOVL addr+0(FP), BP
TESTL , BP
JZ 2(PC)
MOVL 0, AX // crash with nil ptr deref
// DI:SI = delta
MOVL delta_lo+4(FP), SI
MOVL delta_hi+8(FP), DI
// DX:AX = *addr
MOVL 0(BP), AX
MOVL 4(BP), DX
addloop:
// CX:BX = DX:AX (*addr) + DI:SI (delta)
MOVL AX, BX
MOVL DX, CX
ADDL SI, BX
ADCL DI, CX
// if *addr == DX:AX {
// *addr = CX:BX
// } else {
// DX:AX = *addr
// }
// all in one instruction
LOCK
CMPXCHG8B 0(BP)
JNZ addloop
// success
// return CX:BX
MOVL BX, new_lo+12(FP)
MOVL CX, new_hi+16(FP)
RET
这可能会引发一个问题:为什么不使用带锁的结构?
编辑以回答评论中的问题:是的,使用 32 位整数将导致在所有 Go 支持的架构上进行实际的原子操作,因为它们都支持 XADDL
(或模拟)到我最好的知识.
这段 Go 代码是否正确且可移植,我需要在一个变量中存储 2 个计数器(每次调用只会更新一个计数器)以避免在我将要使用单个 atomic.AddUint64() 的实际代码中锁定而不是锁定整个结构。
package main
import "fmt"
var long uint64 // Actual counters storage
func main() {
left := uint32(100) // First counter
right := uint32(200) // Second counter
long = uint64(left)
long = long << 32 | uint64(right)
fmt.Println(left, right)
long += uint64(1 << 32) // Increment left
long += 1 // Increment right
xLeft := uint32(long >> 32) // Get left
xRight := uint32(long) // Get right
fmt.Println(xLeft, xRight)
}
Is this Go code correct and portable
这是正确的,只要您使用 64 位宽度的无符号整数。
在这种情况下,可移植性由 sync/atomic
包提供给 Go 编译器支持的体系结构。但是请注意,并非所有体系结构都支持对 64 位宽数据进行 "true" 原子操作。例如,the i386 implementation 使用 CAS 循环:
TEXT ·AddUint64(SB),NOSPLIT,[=10=]-20
// no XADDQ so use CMPXCHG8B loop
MOVL addr+0(FP), BP
TESTL , BP
JZ 2(PC)
MOVL 0, AX // crash with nil ptr deref
// DI:SI = delta
MOVL delta_lo+4(FP), SI
MOVL delta_hi+8(FP), DI
// DX:AX = *addr
MOVL 0(BP), AX
MOVL 4(BP), DX
addloop:
// CX:BX = DX:AX (*addr) + DI:SI (delta)
MOVL AX, BX
MOVL DX, CX
ADDL SI, BX
ADCL DI, CX
// if *addr == DX:AX {
// *addr = CX:BX
// } else {
// DX:AX = *addr
// }
// all in one instruction
LOCK
CMPXCHG8B 0(BP)
JNZ addloop
// success
// return CX:BX
MOVL BX, new_lo+12(FP)
MOVL CX, new_hi+16(FP)
RET
这可能会引发一个问题:为什么不使用带锁的结构?
编辑以回答评论中的问题:是的,使用 32 位整数将导致在所有 Go 支持的架构上进行实际的原子操作,因为它们都支持 XADDL
(或模拟)到我最好的知识.