Go语言中的原子操作能确保其他变量对其他线程可见吗?

aiqt4smr  于 2023-08-01  发布在  Go
关注(0)|答案(2)|浏览(110)

它使我困惑,我阅读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将在不同的goroutine中可见。
在java中,我知道AtomicInteger,内存屏障由“volatile”组成,保持线程字段可见。

csga3l58

csga3l581#

Go没有volatile等价物。原子内存模型在Go语言中没有很好的定义,所以为了超级安全,你应该什么都不做,也就是说。对a的改变可以是不可见的。但在实践中,正如我所理解的,所有体系结构都有一个内存围栏,所以你是安全的。
有一个big issue关于定义行为,Russ考克斯评论说
是的,去年冬天我花了一段时间写这个,但还没有机会把它写好。简短的版本是,我相当肯定的规则是,Go的原子保证原子变量之间的顺序一致性(表现得像C/C++的seqconst原子),并且对于给定的内存字,你不应该混合原子和非原子访问。
相关答案https://stackoverflow.com/a/58892365/2133484

czq61nw1

czq61nw12#

测试程序:

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, $0-0
    0x0000 00000 (main.go:9)    FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0000 00000 (main.go:9)    FUNCDATA    $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0000 00000 (main.go:10)   MOVL    $100, "".n(SB)
    0x000a 00010 (main.go:11)   MOVL    $1, 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#信号在伴随指令的执行期间被Assert(将指令变成原子指令)。在多处理器环境中,LOCK#信号确保在信号被Assert时处理器独占使用任何共享存储器。在大多数IA-32和所有Intel 64处理器中,锁定可以在没有LOCK#信号被Assert的情况下发生。有关详细信息,请参阅下面的“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前缀与上述列表之外的任何指令一起使用,也会生成未定义的操作码异常。XCHG指令总是AssertLOCK#信号,而不管LOCK前缀的存在与否。LOCK前缀通常与BTS指令一起使用,以对共享存储器环境中的存储器位置执行读取-修改-写入操作。LOCK前缀的完整性不受内存字段对齐的影响。内存锁定观察任意错位的领域。此指令在非64位模式和64位模式下的操作相同。*

所以,是的,它具有内存可见性。

相关问题