Go 编译器是否优化了代码?
在此代码中:
package main import "time" func main() { i := 1 go func() { for { i++ } }() <-time.After(1 * time.Second) println(i) }
输出始终为 1然而,令人惊讶的是,1s 足以让 for 循环执行多次。原因是 Go 编译器正在优化代码。
Go 内存模型指定了在一个 goroutine 中读取变量时可以保证观察到写入同一变量所产生的值的条件。一个不同的 goroutine。通过增量 i (i = i 1) 对 i 进行赋值后不会发生任何同步事件,因此不能保证任何其他 goroutine 都会观察到它。事实上,激进的编译器可能会删除整个 i 语句。
例如,在这段代码中:
package main import "time" func main() { i := 1 go func() { for { i++ } }() <-time.After(1 * time.Millisecond) println(i) }
输出为 1。 goroutine 被简化为:
"".main.func1 STEXT nosplit size=2 args=0x8 locals=0x0 0x0000 00000 (elide.go:7) TEXT "".main.func1(SB), NOSPLIT, <pre class="brush:php;toolbar:false">for { i++ }
package main import "time" func main() { i := 1 go func() { for { i++ println("+1") } }() <-time.After(1 * time.Millisecond) println(i) }
对于编译器来说,for 循环可以通过永远递增寄存器来实现,本质上是一个无操作的 for 循环:
+1 +1 << SNIP >> +1 +1 432
插入 print 语句后,
"".main.func1 STEXT size=81 args=0x8 locals=0x18 0x0000 00000 (elide.go:7) TEXT "".main.func1(SB), -8 0x0000 00000 (elide.go:7) MOVQ (TLS), CX 0x0009 00009 (elide.go:7) CMPQ SP, 16(CX) 0x000d 00013 (elide.go:7) JLS 74 0x000f 00015 (elide.go:7) SUBQ , SP 0x0013 00019 (elide.go:7) MOVQ BP, 16(SP) 0x0018 00024 (elide.go:7) LEAQ 16(SP), BP 0x001d 00029 (elide.go:7) FUNCDATA <pre class="brush:php;toolbar:false">================== WARNING: DATA RACE Read at 0x00c420094000 by main goroutine: main.main() /home/peter/gopath/src/lucky.go:14 +0xac Previous write at 0x00c420094000 by goroutine 5: main.main.func1() /home/peter/gopath/src/lucky.go:9 +0x4e Goroutine 5 (running) created at: main.main() /home/peter/gopath/src/lucky.go:7 +0x7a ==================
package main import ( "sync" "time" ) func main() { mx := new(sync.Mutex) i := 1 go func() { for { mx.Lock() i++ mx.Unlock() } }() <-time.After(1 * time.Second) mx.Lock() println(i) mx.Unlock() }
41807838
输出为:
goroutine 扩展为:
goroutine 复杂性的增加意味着编译器不再考虑将寄存器专用于我。 i 的内存值会递增,这使得主 Goroutine 通过数据竞争使更新可见。
要获得预期结果,请添加一些同步:
输出:
以上是Go 编译器是否会优化 goroutine 中递增变量的代码?的详细内容。更多信息请关注PHP中文网其他相关文章!