Hinweis:
Ab Go 1.5 ist GOMAXPROCS auf die Anzahl der Kerne der Hardware eingestellt: golang.org/doc/go1.5#runtime , unterhalb der ursprünglichen Antwort vor 1.5.
Wenn Sie das Go-Programm ausführen, ohne die Umgebungsvariable GOMAXPROCS anzugeben, werden Go-Goroutinen für die Ausführung in einem einzelnen Betriebssystem-Thread geplant. Um das Programm jedoch als Multithread-Programm erscheinen zu lassen (dafür sind Goroutinen gedacht, nicht wahr?), Muss der Go-Scheduler manchmal den Ausführungskontext wechseln, damit jede Goroutine ihre Arbeit erledigen kann.
Wie gesagt, wenn die GOMAXPROCS-Variable nicht angegeben ist, darf die Go-Laufzeit nur einen Thread verwenden, sodass es unmöglich ist, den Ausführungskontext zu wechseln, während Goroutine einige konventionelle Arbeiten wie Berechnungen oder sogar E / A ausführt (die einfachen C-Funktionen zugeordnet sind) ). Der Kontext kann nur umgeschaltet werden, wenn Go-Parallelitätsprimitive verwendet werden, z. B. wenn Sie mehrere Chans einschalten oder (dies ist Ihr Fall) wenn Sie den Scheduler ausdrücklich anweisen, die Kontexte zu wechseln - dafür ist dies vorgesehen runtime.Gosched
.
Kurz gesagt, wenn der Ausführungskontext in einer Goroutine den Gosched
Aufruf erreicht , wird der Scheduler angewiesen, die Ausführung auf eine andere Goroutine umzuschalten. In Ihrem Fall gibt es zwei Goroutinen, main (der 'Haupt'-Thread des Programms darstellt) und zusätzlich die, mit der Sie erstellt haben go say
. Wenn Sie den Gosched
Anruf entfernen , wird der Ausführungskontext niemals von der ersten Goroutine zur zweiten übertragen, daher keine "Welt" für Sie. Wenn Gosched
vorhanden, überträgt der Scheduler die Ausführung bei jeder Schleifeniteration von der ersten Goroutine zur zweiten und umgekehrt, sodass Sie "Hallo" und "Welt" verschachtelt haben.
Zu Ihrer Information, dies wird als "kooperatives Multitasking" bezeichnet: Goroutinen müssen die Kontrolle explizit anderen Goroutinen überlassen. Der in den meisten modernen Betriebssystemen verwendete Ansatz wird als "präemptives Multitasking" bezeichnet: Ausführungsthreads befassen sich nicht mit der Übertragung von Steuerelementen. Der Scheduler wechselt stattdessen transparent zu ihnen die Ausführungskontexte. Ein kooperativer Ansatz wird häufig verwendet, um "grüne Threads" zu implementieren, dh logische gleichzeitige Coroutinen, die OS-Threads nicht 1: 1 zuordnen. Auf diese Weise werden die Go-Laufzeit und ihre Goroutinen implementiert.
Aktualisieren
Ich habe die Umgebungsvariable GOMAXPROCS erwähnt, aber nicht erklärt, was es ist. Es ist Zeit, dies zu beheben.
Wenn diese Variable auf eine positive Zahl gesetzt ist N
, kann Go Runtime bis zu N
native Threads erstellen , auf denen alle grünen Threads geplant werden. Nativer Thread Eine Art Thread, der vom Betriebssystem erstellt wird (Windows-Threads, Pthreads usw.). Dies bedeutet, N
dass Goroutinen , wenn sie größer als 1 sind, möglicherweise in verschiedenen nativen Threads ausgeführt werden und folglich parallel ausgeführt werden (zumindest bis zu Ihren Computerfähigkeiten: Wenn Ihr System auf einem Multicore-Prozessor basiert, ist dies der Fall Es ist wahrscheinlich, dass diese Threads wirklich parallel sind. Wenn Ihr Prozessor über einen einzelnen Kern verfügt, führt das in OS-Threads implementierte präemptive Multitasking zu einer Sichtbarkeit der parallelen Ausführung.
Es ist möglich, die Variable GOMAXPROCS mithilfe der runtime.GOMAXPROCS()
Funktion festzulegen, anstatt die Umgebungsvariable voreinzustellen . Verwenden Sie in Ihrem Programm so etwas anstelle des aktuellen main
:
func main() {
runtime.GOMAXPROCS(2)
go say("world")
say("hello")
}
In diesem Fall können Sie interessante Ergebnisse beobachten. Es ist möglich, dass Sie "Hallo" - und "Welt" -Linien ungleichmäßig verschachtelt drucken lassen, z
hello
hello
world
hello
world
world
...
Dies kann passieren, wenn Goroutinen OS-Threads trennen sollen. So funktioniert präventives Multitasking (oder Parallelverarbeitung bei Multicore-Systemen): Threads sind parallel und ihre kombinierte Ausgabe ist unbestimmt. Übrigens, Sie können den Gosched
Anruf verlassen oder entfernen . Es scheint keine Auswirkung zu haben, wenn GOMAXPROCS größer als 1 ist.
Folgendes habe ich bei mehreren Programmläufen mit runtime.GOMAXPROCS
Aufruf erhalten.
hyperplex /tmp % go run test.go
hello
hello
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
hello
hello
hello
hello
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
Sehen Sie, manchmal ist die Ausgabe hübsch, manchmal nicht. Indeterminismus in Aktion :)
Ein weiteres Update
In neueren Versionen des Go-Compilers sieht die Go-Laufzeit aus, dass Goroutinen nicht nur bei der Verwendung von Parallelitätsprimitiven, sondern auch bei Systemaufrufen des Betriebssystems nachgeben. Dies bedeutet, dass der Ausführungskontext auch bei E / A-Funktionsaufrufen zwischen Goroutinen umgeschaltet werden kann. Folglich ist es in neueren Go-Compilern möglich, unbestimmtes Verhalten zu beobachten, selbst wenn GOMAXPROCS nicht gesetzt oder auf 1 gesetzt ist.
Gosched()
benötigt wird oder nicht , hängt von Ihrem Programm ab, es hängt nicht vomGOMAXPROCS
Wert ab. Die Effizienz des präventiven Multitasking gegenüber dem kooperativen hängt auch von Ihrem Programm ab. Wenn Ihr Programm E / A-gebunden ist, ist kooperatives Multitasking mit asynchronen E / A wahrscheinlich effizienter (dh hat einen höheren Durchsatz) als synchrone Thread-basierte E / A. Wenn Ihr Programm CPU-gebunden ist (z. B. lange Berechnungen), ist kooperatives Multitasking viel weniger nützlich.Die kooperative Planung ist der Schuldige. Ohne nachzugeben, kann die andere (sagen wir "Welt") Goroutine legal keine Chance haben, vor / nach dem Ende von main ausgeführt zu werden, was laut Spezifikation alle Gorutinen beendet - dh. der ganze Prozess.
quelle
runtime.Gosched()
gibt nach. Was bedeutet das? Gibt es die Steuerung zurück zur Hauptfunktion?