Nota:
A partire da Go 1.5, GOMAXPROCS è impostato sul numero di core dell'hardware: golang.org/doc/go1.5#runtime , sotto la risposta originale prima della 1.5.
Quando si esegue il programma Go senza specificare la variabile di ambiente GOMAXPROCS, le goroutine Go vengono pianificate per l'esecuzione in un singolo thread del sistema operativo. Tuttavia, per fare in modo che il programma sembri multithread (è a questo che servono le goroutine, non è vero?), Lo scheduler di Go a volte deve cambiare il contesto di esecuzione, quindi ogni goroutine potrebbe fare il suo lavoro.
Come ho detto, quando la variabile GOMAXPROCS non è specificata, il runtime Go può utilizzare solo un thread, quindi è impossibile cambiare i contesti di esecuzione mentre goroutine sta eseguendo un lavoro convenzionale, come calcoli o anche IO (che è mappato a semplici funzioni C ). Il contesto può essere cambiato solo quando vengono usate le primitive di concorrenza Go, ad esempio quando si attivano più canali, o (questo è il tuo caso) quando dici esplicitamente allo scheduler di cambiare contesto - questo è ciò che runtime.Goschedserve.
Quindi, in breve, quando il contesto di esecuzione in una goroutine raggiunge la Goschedchiamata, allo scheduler viene chiesto di cambiare l'esecuzione in un'altra goroutine. Nel tuo caso ci sono due goroutine, principale (che rappresenta il thread "principale" del programma) e aggiuntivo, quella con cui hai creato go say. Se rimuovi la Goschedchiamata, il contesto di esecuzione non verrà mai trasferito dalla prima goroutine alla seconda, quindi nessun "mondo" per te. Quando Goschedè presente, lo scheduler trasferisce l'esecuzione su ogni iterazione del ciclo dalla prima goroutine alla seconda e viceversa, in modo da avere 'hello' e 'world' interleaved.
Cordiali saluti, questo è chiamato "multitasking cooperativo": le goroutine devono cedere esplicitamente il controllo ad altre goroutine. L'approccio utilizzato nella maggior parte dei sistemi operativi contemporanei è chiamato "multitasking preemptive": i thread di esecuzione non si occupano del trasferimento del controllo; lo scheduler cambia invece i contesti di esecuzione in modo trasparente. L'approccio cooperativo viene spesso utilizzato per implementare "thread verdi", ovvero coroutine logiche concorrenti che non mappano 1: 1 ai thread del sistema operativo: questo è il modo in cui vengono implementati il runtime di Go e le sue goroutine.
Aggiornare
Ho menzionato la variabile d'ambiente GOMAXPROCS ma non ho spiegato di cosa si tratta. È ora di risolvere questo problema.
Quando questa variabile è impostata su un numero positivo N, il runtime di Go sarà in grado di creare fino a Nthread nativi, su cui verranno pianificati tutti i thread verdi. Thread nativo un tipo di thread creato dal sistema operativo (thread Windows, pthreads ecc.). Ciò significa che se Nè maggiore di 1, è possibile che le goroutine siano programmate per essere eseguite in diversi thread nativi e, di conseguenza, eseguite in parallelo (almeno, fino alle capacità del tuo computer: se il tuo sistema è basato su un processore multicore, è probabile che questi thread saranno veramente paralleli; se il tuo processore ha un singolo core, il multitasking preventivo implementato nei thread del sistema operativo creerà una visibilità dell'esecuzione parallela).
È possibile impostare la variabile GOMAXPROCS utilizzando la runtime.GOMAXPROCS()funzione invece di preimpostare la variabile d'ambiente. Usa qualcosa di simile nel tuo programma invece dell'attuale main:
func main() {
runtime.GOMAXPROCS(2)
go say("world")
say("hello")
}
In questo caso puoi osservare risultati interessanti. È possibile che le righe "ciao" e "mondo" vengano stampate in modo non uniforme, ad es
hello
hello
world
hello
world
world
...
Ciò può accadere se le goroutine sono programmate per separare i thread del sistema operativo. Questo è infatti il modo in cui funziona il multitasking preventivo (o elaborazione parallela nel caso di sistemi multicore): i thread sono paralleli e il loro output combinato è indeterministico. BTW, puoi lasciare o rimuovere la Goschedchiamata, sembra che non abbia effetto quando GOMAXPROCS è maggiore di 1.
Quello che segue è quello che ho ottenuto su diverse esecuzioni del programma con runtime.GOMAXPROCScall.
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
Vedi, a volte l'output è carino, a volte no. Indeterminismo in azione :)
Un altro aggiornamento
Sembra che nelle versioni più recenti del compilatore Go Go runtime costringa le goroutine a cedere non solo sull'utilizzo delle primitive di concorrenza, ma anche sulle chiamate di sistema del sistema operativo. Ciò significa che il contesto di esecuzione può essere commutato tra le goroutine anche su chiamate di funzioni IO. Di conseguenza, nei recenti compilatori Go è possibile osservare un comportamento indeterministico anche quando GOMAXPROCS non è impostato o è impostato su 1.