Le compilateur Go optimise-t-il le code ?
Dans ce code :
package main import "time" func main() { i := 1 go func() { for { i++ } }() <-time.After(1 * time.Second) println(i) }
La sortie est toujours 1 Cependant, il est surprenant que 1s soit suffisant pour que la boucle for soit répétée plusieurs fois. La raison en est que le compilateur Go optimise le code.
Le modèle de mémoire Go spécifie les conditions dans lesquelles les lectures d'une variable dans une goroutine peuvent être garanties pour observer les valeurs produites par les écritures dans la même variable dans une goroutine différente. L'affectation à i, via l'incrément i (i = i 1), n'est suivie d'aucun événement de synchronisation, il n'est donc pas garanti qu'elle soit observée par une autre goroutine. En fait, un compilateur agressif pourrait supprimer l'intégralité de l'instruction i.
Par exemple, dans ce code :
package main import "time" func main() { i := 1 go func() { for { i++ } }() <-time.After(1 * time.Millisecond) println(i) }
La sortie est 1. La goroutine est réduite à :
"".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) }
Pour le compilateur, la boucle for peut être implémentée en incrémentant un registre pour toujours, essentiellement une boucle for sans opération :
+1 +1 << SNIP >> +1 +1 432
Après avoir inséré une instruction d'impression,
"".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
Le résultat est :
La goroutine se développe en :
La complexité accrue de la goroutine signifie que le compilateur n'envisage plus de dédier un registre à la valeur de je. La valeur en mémoire de i est incrémentée, ce qui rend les mises à jour visibles, avec une course aux données, à la goroutine principale.
Pour obtenir le résultat attendu, ajoutez un peu de synchronisation :
Sortie :
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!