105 votes

Que fait exactement runtime.Gosched?

Sur le site Web Tour of Go , il y a un morceau de code qui ressemble à ceci.

 package main

import (
    "fmt"
    "runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say("world")
    say("hello")
}
 

La sortie ressemble à ceci:

 hello
world
hello
world
hello
world
hello
world
hello
 

Ce qui me dérange, c’est que lorsque runtime.Gosched() est supprimé, le programme n’imprime plus le mot "monde".

 hello
hello
hello
hello
hello
 

Pourquoi est-ce si? Comment runtime.Gosched() affecte-t-il l'exécution?

168voto

Vladimir Matveev Points 16593

Lorsque vous exécutez le programme de go, sans préciser GOMAXPROCS variable d'environnement, d'Aller goroutines sont prévus pour l'exécution du système d'exploitation unique thread. Cependant, pour faire de programme semblent être multithread (c'est ce que goroutines sont pour, n'est-ce pas?), l'Aller planificateur doit parfois changer le contexte d'exécution, de sorte que chaque goroutine pourrait faire sa part de travail.

Comme je l'ai dit, quand GOMAXPROCS variable n'est pas spécifié, Aller de l'exécution n'est autorisé à utiliser un fil, de sorte qu'il est impossible de mettre des contextes d'exécution tout en goroutine est de l'exécution de certains travaux conventionnels, comme les calculs ou même IO (qui est mappé à la plaine de fonctions C). Le contexte peut être changé que lorsque Aller simultanéité primitives sont utilisées, par exemple, lorsque vous basculez sur plusieurs chans, ou (ce qui est votre cas) quand vous indiquer explicitement le planificateur de changer de contexte - c'est ce qu' runtime.Gosched est pour.

Donc, en résumé, lorsque le contexte d'exécution dans une goroutine atteint Gosched appel, le planificateur est chargé de commutateur de l'exécution à l'autre, goroutine. Dans votre cas, il y a deux goroutines, principal (qui représente le principal fil conducteur du programme) et les autres, celui que vous avez créé avec go say. Si vous supprimez Gosched appel, le contexte d'exécution ne seront jamais cédées à partir de la première goroutine à la seconde, donc pas de "monde" pour vous. Lors de l' Gosched est présent, le transfère l'exécution de chaque itération de boucle à partir de la première goroutine à la seconde et vice-versa, de sorte que vous avez "bonjour" et "monde" entrelacés.

Pour info, cela est appelé "le multitâche coopératif': goroutines doit explicitement céder le contrôle à d'autres goroutines. L'approche utilisée dans la plupart des contemporains de Systèmes d'exploitation s'appelle 'le multitâche préemptif': les threads d'exécution ne sont pas concernés par le contrôle de transfert; le planificateur de commutateurs de contextes d'exécution de manière transparente à la place. Approche coopérative est souvent utilisé pour mettre en œuvre les fils verts", c'est logique simultanées coroutines qui ne sont pas de carte 1:1 pour l'OS threads - ce est de savoir comment Aller de l'exécution et de ses goroutines sont mis en œuvre.

Mise à jour

Je l'ai mentionné GOMAXPROCS variable d'environnement mais n'a pas expliqué ce qu'il est. Il est temps de corriger cela.

Lorsque cette variable est définie sur un nombre positif N, Aller de l'exécution sera capable de créer jusqu'à N de threads natifs, sur lequel tous les fils verts seront programmées. Natif des threads un genre de fil qui est créé par le système d'exploitation (Windows, les threads, les pthreads etc). Cela signifie que si N est supérieur à 1, il est possible que les goroutines seront planifiés pour s'exécuter dans les différents threads natifs et, par conséquent, de s'exécuter en parallèle (au moins, jusqu'à votre capacités de l'ordinateur: si votre système est basé sur un processeur multicœur, il est probable que ces discussions seront véritablement parallèle; si votre processeur single core, puis le multitâche préemptif mis en œuvre dans les OS des threads de créer une visibilité de l'exécution en parallèle).

Il est possible de définir GOMAXPROCS variable à l'aide de runtime.GOMAXPROCS() fonction au lieu de pré-réglage de la variable d'environnement. Utiliser quelque chose comme ceci dans votre programme à la place de l'actuel main:

func main() {
    runtime.GOMAXPROCS(2)
    go say("world")
    say("hello")
}

Dans ce cas, vous pouvez observer des résultats intéressants. Il est possible que vous obtiendrez un "bonjour" et "monde" lignes imprimées entrelacés de façon inégale, par exemple

hello
hello
world
hello
world
world
...

Cela peut se produire si goroutines sont prévues pour séparer les OS des threads. C'est en fait comment multitâche préemptif œuvres ou de traitement en parallèle dans le cas des systèmes multiprocesseurs): les fils sont parallèles, et leur production combinée est indeterministic. BTW, vous pouvez laisser ou supprimer Gosched appel, il ne semble avoir aucun effet lorsque GOMAXPROCS est plus grand que 1.

Ce qui suit est ce que j'ai obtenu sur plusieurs points du programme avec runtime.GOMAXPROCS appel.

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

Voir, parfois, la sortie est joli, parfois pas. Indéterminisme dans l'action :)

Une autre mise à jour

Il semble que dans les versions plus récentes d'Aller compilateur Aller runtime forces goroutines à rendement non seulement sur la simultanéité des primitives de l'usage, mais sur le système d'exploitation appelle aussi. Cela signifie que le contexte d'exécution peut être commuté entre les goroutines également sur IO appels de fonctions. En conséquence, ces dernières Vont compilateurs, il est possible d'observer indeterministic comportement, même quand GOMAXPROCS est pas défini ou la valeur 1.

10voto

zzzz Points 23017

La planification coopérative est le coupable. Sans céder, l'autre (par exemple "monde") goroutine peut légalement avoir zéro chance de s'exécuter avant / quand principal se termine, ce qui, selon les spécifications, termine tous les gorutines - c'est-à-dire. Le processus complet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X