Verständnis goroutines
Ich versuche zu verstehen, die Parallelität Gehen. Insbesondere, schrieb ich diesen thread-unsafe-Programm:
package main
import "fmt"
var x = 1
func inc_x() { //test
for {
x += 1
}
}
func main() {
go inc_x()
for {
fmt.Println(x)
}
}
Erkenne ich, dass ich mit sollte-Kanäle um zu verhindern, dass race-conditions mit x
, aber das ist nicht der Punkt hier. Das Programm druckt 1
und dann scheint loop forever (ohne Druck nichts mehr). Ich würde erwarten, dass es zu drucken, die eine unendliche Liste der zahlen, möglicherweise überspringen einiger und wiederholen der anderen wegen der race condition (oder noch schlimmer -- drucken der Zahl, während es aktualisiert wird, in inc_x
).
Meine Frage ist: Warum funktioniert das Programm nur drucken einer Zeile?
Nur um klar zu sein: ich bin nicht mit Kanälen auf Zweck für dieses Spielzeug Beispiel.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Gibt es ein paar Dinge zu beachten, über Go ' s goroutines:
GOMAXPROCS
und standardmäßig 1 aktuell, denke ich. Dies kann sich in Zukunft ändernLaufzeit.Gosched()
explizit Steuern Erträge zurück zum threadDas Verhalten, das Sie sehen, ist, weil die main-Funktion niemals die Renditen zurück auf den thread und ist vielmehr eingebunden in einen busy-loop, und da ist nur ein thread die main-loop hat keinen Platz, zu laufen.
Laut diese und diese, einige Anrufe können nicht aufrufen, während ein CPU-gebunden Goroutine (wenn die Goroutine nie Erträge, die der Planer). Dies kann dazu führen, dass andere Goroutines zu hängen, wenn Sie benötigen, um die Sperrung der Haupt-thread (dies ist der Fall mit der
write()
Systemaufruf verwendetfmt.Println()
)Die Lösung, die ich gefunden beteiligt aufrufen
Laufzeit.Gosched()
in Ihrer cpu-bound-thread Ausbeute zurück an den scheduler wie folgt:Weil Sie nur die Durchführung einer operation in der Goroutine,
runtime.Gosched()
aufgerufen wird sehr oft. Aufrufruntime.GOMAXPROCS(2)
auf init wird schneller durch eine um eine Größenordnung, wäre aber sehr thread-unsicher, wenn Sie etwas komplizierter ist als das Inkrementieren einer Zahl (zum Beispiel, Umgang mit arrays, structs, Karten, etc).In diesem Fall, best-practice-würde möglicherweise ein Programm zum verwalten von freigegebenen Zugriff auf eine Ressource.
Update: Wie der 1.2 Gehen, alle nicht-inline-Funktion aufrufen, aufrufen können Sie den scheduler.
Es ist eine Interaktion von zwei Dingen. Eine standardmäßig Gehen, nutzt nur ein single core, und zwei, Gehen planen müssen goroutines kooperativ. Ihre Funktion inc_x nicht nachgeben und so reißt die single-core verwendet wird. Entlastung eine dieser Bedingungen führt zu der Ausgabe, die Sie erwarten.
Sagen "core" ist ein bisschen wie ein gloss. Gehen tatsächlich mehrere Kerne hinter den kulissen, aber es nutzt eine variable namens GOMAXPROCS, um zu bestimmen, die Anzahl der threads, planen Sie Ihre goroutines die sind, die nicht-system-tasks. Wie bereits in den FAQ und Effektiv Gehen der Standardwert ist 1, aber es kann höher eingestellt werden mit einer Umgebungsvariablen oder Laufzeit-Funktion. Dies wird wahrscheinlich geben Sie die Ausgabe, die Sie erwarten, aber nur, wenn Ihr Prozessor hat mehrere Kerne.
Unabhängig von Kernen und GOMAXPROCS, können Sie die goroutine scheduler, die in der Laufzeit eine chance, es zu tun job. Der scheduler kann nicht der Auslöser einer Laufenden goroutine, sondern muss warten, bis es wieder zu kommen, um die Laufzeit und die Anfrage einen Dienst, wie IO, die Zeit.Sleep () oder runtime.Gosched(). Etwas hinzuzufügen, wie dies in inc_x erzeugt die erwartete Ausgabe. Die goroutine ausführen von main() ist bereits beantragt, ein service mit fmt.Println, also mit den zwei goroutines jetzt in regelmäßigen Abständen was auf die Laufzeit, es kann eine Art gerechte Planung.
Nicht sicher, aber ich denke, dass
inc_x
ist die CPU in Beschlag. Da es keine IO es nicht, die Kontrolle abzugeben.Fand ich zwei Dinge, die löste das Problem. Man war zu nennen
runtime.GOMAXPROCS(2)
zu Beginn des Programms und dann werde es funktionieren, da gibt es jetzt zwei threads dienen goroutings. Das andere ist das einfügentime.Sleep(1)
nach der Inkrementierungx
.