Introduction
The Go memory model specifies the conditions under which reads of a variable in one goroutine can be guaranteed to observe values produced by writes to the same variable in a different goroutine.
Advice
Programs that modify data being simultaneously accessed by multiple goroutines must serialize such access.
To serialize access, protect the data with channel operations or other synchronization primitives such as those in the sync and sync/atomic packages.
If you must read the rest of this document to understand the behavior of your program, you are being too clever.
Don't be clever.
使用 go tool compile -S ./main.go 来获 取汇编结果。在下面的输出结果中,s = "I am string" 赋值语句会被拆成两部分: 先 更新字符串的长度 len 字段, 再更新具体的字符串内容到 str 字段。
"".main.func1 STEXT size=89 args=0x8 locals=0x8
0x0000 00000 (./main.go:5) TEXT "".main.func1(SB), ABIInternal, $8-8
0x0000 00000 (./main.go:5) MOVQ (TLS), CX
0x0009 00009 (./main.go:5) CMPQ SP, 16(CX)
0x000d 00013 (./main.go:5) JLS 82
0x000f 00015 (./main.go:5) SUBQ $8, SP
0x0013 00019 (./main.go:5) MOVQ BP, (SP)
0x0017 00023 (./main.go:5) LEAQ (SP), BP
0x001b 00027 (./main.go:5) FUNCDATA $0, gclocals·1a65e721a2ccc325b382662e7ffee780(SB)
0x001b 00027 (./main.go:5) FUNCDATA $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
0x001b 00027 (./main.go:5) FUNCDATA $2, gclocals·39825eea4be6e41a70480a53a624f97b(SB)
0x001b 00027 (./main.go:6) PCDATA $0, $1
0x001b 00027 (./main.go:6) PCDATA $1, $1
0x001b 00027 (./main.go:6) MOVQ "".&s+16(SP), DI
先更新长度 0x0020 00032 (./main.go:6) MOVQ $11, 8(DI)
0x0028 00040 (./main.go:6) PCDATA $0, $-2
0x0028 00040 (./main.go:6) PCDATA $1, $-2
0x0028 00040 (./main.go:6) CMPL runtime.writeBarrier(SB), $0
0x002f 00047 (./main.go:6) JNE 68
再赋值内容 0x0031 00049 (./main.go:6) LEAQ go.string."I am string"(SB), AX
0x0038 00056 (./main.go:6) MOVQ AX, (DI)
0x003b 00059 (./main.go:7) MOVQ (SP), BP
0x003f 00063 (./main.go:7) ADDQ $8, SP
0x0043 00067 (./main.go:7) RET
0x0044 00068 (./main.go:6) LEAQ go.string."I am string"(SB), AX
0x004b 00075 (./main.go:6) CALL runtime.gcWriteBarrier(SB)
0x0050 00080 (./main.go:6) JMP 59
0x0052 00082 (./main.go:6) NOP
0x0052 00082 (./main.go:5) PCDATA $1, $-1
0x0052 00082 (./main.go:5) PCDATA $0, $-1
0x0052 00082 (./main.go:5) CALL runtime.morestack_noctxt(SB)
0x0057 00087 (./main.go:5) JMP 0
NOTE: runtime.xxxBarrier 是 Go 编译器为垃圾回收生成的代码,可以忽略。
回到一开始的问题 example 2 代码片段,r = append(r, s...) 采用 memmove 方法从字符串 s 拷贝 len(s) 个字节到 r 里。由于 s = "panic?" 赋值和 append 读 操作是同时进行:假设 s.len 已经被更新成 6 ,但是 s.str 还是 nil 状态,这个时候 正好执行了 append 的操作,直接读取空指针必定会 panic