Go 中的原子操作是否确保其他变量对其他线程可见?
Do atomic operations in Go make sure other variables are visible to other threads?
这让我很困惑,我正在阅读 golang 内存模型,https://golang.org/ref/mem
var l sync.Mutex
var a string
func f() {
a = "hello, world"
l.Unlock()
}
func main() {
l.Lock()
go f()
l.Lock()
print(a)
}
互斥锁原子解锁
UnLock: new := atomic.AddInt32(&m.state, -mutexLocked)
Lock: atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked)
我的问题是,如果原子AddInt32,CompareAndSwapInt32会造成内存障碍,如果a
会在不同的goroutines中可见
在java中,我知道AtomicInteger,内存屏障是“volatile”,保持线程字段可见。
Go 没有 volatile 等价物。 Go 中的原子内存模型没有很好的定义,所以为了超级安全,你应该什么都不做,即 a
的变化是不可见的。但在实践中,据我所知,所有架构都会做一个内存栅栏,所以你是安全的。
有big issue about defining the behavior, there's a comment from Russ Cox说法
Yes, I spent a while on this last winter but didn't get a chance to write it up properly yet. The short version is that I'm fairly certain the rules will be that Go's atomics guarantee sequential consistency among the atomic variables (behave like C/C++'s seqconst atomics), and that you shouldn't mix atomic and non-atomic accesses for a given memory word.
相关回答
测试程序:
package main
import (
"sync/atomic"
)
var n uint32
func main() {
n = 100
atomic.AddUint32(&n, 1)
}
通过以下方式检查程序集:
go tool compile -S main.go
"".main STEXT nosplit size=27 args=0x0 locals=0x0 funcid=0x0
0x0000 00000 (main.go:9) TEXT "".main(SB), NOSPLIT|ABIInternal, [=11=]-0
0x0000 00000 (main.go:9) FUNCDATA [=11=], gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:9) FUNCDATA , gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:10) MOVL 0, "".n(SB)
0x000a 00010 (main.go:11) MOVL , AX
0x000f 00015 (main.go:11) LEAQ "".n(SB), CX
0x0016 00022 (main.go:11) LOCK
0x0017 00023 (main.go:11) XADDL AX, (CX)
0x001a 00026 (main.go:12) RET
0x0000 c7 05 00 00 00 00 64 00 00 00 b8 01 00 00 00 48 ......d........H
0x0010 8d 0d 00 00 00 00 f0 0f c1 01 c3 ...........
rel 2+4 t=15 "".n+-4
rel 18+4 t=15 "".n+0
go.cuinfo.packagename. SDWARFCUINFO dupok size=0
0x0000 6d 61 69 6e main
""..inittask SNOPTRDATA size=24
0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0010 00 00 00 00 00 00 00 00 ........
"".n SNOPTRBSS size=4
type..importpath.sync/atomic. SRODATA dupok size=13
0x0000 00 0b 73 79 6e 63 2f 61 74 6f 6d 69 63 ..sync/atomic
gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
0x0000 01 00 00 00 00 00 00 00
LOCK指令is:
导致处理器的 LOCK# 信号在执行伴随指令期间被断言(将指令变成原子指令)。在多处理器环境中,LOCK# 信号确保处理器在该信号有效时独占使用任何共享内存。
在大多数 IA-32 和所有 Intel 64 处理器中,锁定可能会在 LOCK# 信号未断言的情况下发生。有关详细信息,请参阅下面的“IA-32 架构兼容性”部分。
LOCK 前缀只能添加到以下指令之前,并且只能添加到目标操作数是内存操作数的指令形式:ADD、ADC、AND、BTC、BTR、BTS、CMPXCHG、CMPXCH8B、CMPXCHG16B、DEC、INC、 NEG、NOT、OR、SBB、SUB、XOR、XADD 和 XCHG。如果 LOCK 前缀与这些指令之一一起使用并且源操作数是内存操作数,则可能会生成未定义的操作码异常 (#UD)。如果 LOCK 前缀与不在上述列表中的任何指令一起使用,也会生成未定义的操作码异常。无论是否存在 LOCK 前缀,XCHG 指令始终断言 LOCK# 信号。
LOCK 前缀通常与 BTS 指令一起使用,以在共享内存环境中对内存位置执行读取-修改-写入操作。
LOCK 前缀的完整性不受内存字段对齐的影响。对于任意未对齐的字段,会观察到内存锁定。
该指令在非64位模式和64位模式下的操作是一样的。
是的,它具有内存可见性。
这让我很困惑,我正在阅读 golang 内存模型,https://golang.org/ref/mem
var l sync.Mutex
var a string
func f() {
a = "hello, world"
l.Unlock()
}
func main() {
l.Lock()
go f()
l.Lock()
print(a)
}
互斥锁原子解锁
UnLock: new := atomic.AddInt32(&m.state, -mutexLocked)
Lock: atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked)
我的问题是,如果原子AddInt32,CompareAndSwapInt32会造成内存障碍,如果a
会在不同的goroutines中可见
在java中,我知道AtomicInteger,内存屏障是“volatile”,保持线程字段可见。
Go 没有 volatile 等价物。 Go 中的原子内存模型没有很好的定义,所以为了超级安全,你应该什么都不做,即 a
的变化是不可见的。但在实践中,据我所知,所有架构都会做一个内存栅栏,所以你是安全的。
有big issue about defining the behavior, there's a comment from Russ Cox说法
Yes, I spent a while on this last winter but didn't get a chance to write it up properly yet. The short version is that I'm fairly certain the rules will be that Go's atomics guarantee sequential consistency among the atomic variables (behave like C/C++'s seqconst atomics), and that you shouldn't mix atomic and non-atomic accesses for a given memory word.
相关回答
测试程序:
package main
import (
"sync/atomic"
)
var n uint32
func main() {
n = 100
atomic.AddUint32(&n, 1)
}
通过以下方式检查程序集:
go tool compile -S main.go
"".main STEXT nosplit size=27 args=0x0 locals=0x0 funcid=0x0
0x0000 00000 (main.go:9) TEXT "".main(SB), NOSPLIT|ABIInternal, [=11=]-0
0x0000 00000 (main.go:9) FUNCDATA [=11=], gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:9) FUNCDATA , gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:10) MOVL 0, "".n(SB)
0x000a 00010 (main.go:11) MOVL , AX
0x000f 00015 (main.go:11) LEAQ "".n(SB), CX
0x0016 00022 (main.go:11) LOCK
0x0017 00023 (main.go:11) XADDL AX, (CX)
0x001a 00026 (main.go:12) RET
0x0000 c7 05 00 00 00 00 64 00 00 00 b8 01 00 00 00 48 ......d........H
0x0010 8d 0d 00 00 00 00 f0 0f c1 01 c3 ...........
rel 2+4 t=15 "".n+-4
rel 18+4 t=15 "".n+0
go.cuinfo.packagename. SDWARFCUINFO dupok size=0
0x0000 6d 61 69 6e main
""..inittask SNOPTRDATA size=24
0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0010 00 00 00 00 00 00 00 00 ........
"".n SNOPTRBSS size=4
type..importpath.sync/atomic. SRODATA dupok size=13
0x0000 00 0b 73 79 6e 63 2f 61 74 6f 6d 69 63 ..sync/atomic
gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
0x0000 01 00 00 00 00 00 00 00
LOCK指令is:
导致处理器的 LOCK# 信号在执行伴随指令期间被断言(将指令变成原子指令)。在多处理器环境中,LOCK# 信号确保处理器在该信号有效时独占使用任何共享内存。 在大多数 IA-32 和所有 Intel 64 处理器中,锁定可能会在 LOCK# 信号未断言的情况下发生。有关详细信息,请参阅下面的“IA-32 架构兼容性”部分。 LOCK 前缀只能添加到以下指令之前,并且只能添加到目标操作数是内存操作数的指令形式:ADD、ADC、AND、BTC、BTR、BTS、CMPXCHG、CMPXCH8B、CMPXCHG16B、DEC、INC、 NEG、NOT、OR、SBB、SUB、XOR、XADD 和 XCHG。如果 LOCK 前缀与这些指令之一一起使用并且源操作数是内存操作数,则可能会生成未定义的操作码异常 (#UD)。如果 LOCK 前缀与不在上述列表中的任何指令一起使用,也会生成未定义的操作码异常。无论是否存在 LOCK 前缀,XCHG 指令始终断言 LOCK# 信号。 LOCK 前缀通常与 BTS 指令一起使用,以在共享内存环境中对内存位置执行读取-修改-写入操作。 LOCK 前缀的完整性不受内存字段对齐的影响。对于任意未对齐的字段,会观察到内存锁定。 该指令在非64位模式和64位模式下的操作是一样的。
是的,它具有内存可见性。