Maison > Article > développement back-end > Le compilateur Go optimise-t-il le code qui incrémente une variable dans une goroutine ?
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++ }-8 0x0000 00000 (elide.go:7) FUNCDATA
package main import "time" func main() { i := 1 go func() { for { i++ println("+1") } }() <-time.After(1 * time.Millisecond) println(i) }, gclocals·2a5305abe05176240e61b8620e19a815(SB) 0x0000 00000 (elide.go:7) FUNCDATA , gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (elide.go:9) JMP 0
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 ==================, gclocals·a36216b97439c93dafebe03e7f0808b5(SB) 0x001d 00029 (elide.go:7) FUNCDATA , gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (elide.go:8) MOVQ "".&i+32(SP), AX 0x0022 00034 (elide.go:9) INCQ (AX) 0x0025 00037 (elide.go:10) PCDATA
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() },
418078380x0025 00037 (elide.go:10) CALL runtime.printlock(SB) 0x002a 00042 (elide.go:10) LEAQ go.string."+1\n"(SB), AX 0x0031 00049 (elide.go:10) MOVQ AX, (SP) 0x0035 00053 (elide.go:10) MOVQ , 8(SP) 0x003e 00062 (elide.go:10) PCDATA , 0x003e 00062 (elide.go:10) CALL runtime.printstring(SB) 0x0043 00067 (elide.go:10) PCDATA , 0x0043 00067 (elide.go:10) CALL runtime.printunlock(SB) 0x0048 00072 (elide.go:9) JMP 29 0x004a 00074 (elide.go:9) NOP 0x004a 00074 (elide.go:7) PCDATA , $-1 0x004a 00074 (elide.go:7) CALL runtime.morestack_noctxt(SB) 0x004f 00079 (elide.go:7) JMP 0
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!