Fonctionnement du type chan
Nous allons donc voir deux stratégies de partage de l'information par la communication.Ne communiquez pas en partageant la mémoire, mais partagez la mémoire en communicant.
L'équipe Go
Communication vers une routine sans état
package main import ( "net/http" "html/template" ) type Page struct { Title string Valeur string } var templates = template.Must(template.ParseFiles("voir.html")) func main() { in := make(chan string) out := make(chan string) go valeurManagerSansEtat(in, out, "Init") http.HandleFunc("/voir", func(w http.ResponseWriter, r *http.Request) { handlerVoirValeur(w, r, request, out) }) http.HandleFunc("/maj", func(w http.ResponseWriter, r *http.Request) { handlerMajValeur(w, r, in) }) http.Handle("/",http.FileServer(http.Dir("."))) http.ListenAndServe(":9999", nil) }La fonction valeurManagerSansEtat s'exécute en parallèle du reste du code. Elle a pour rôle d'attendre l'arrivée d'une valeur dans le canal in. Losqu'elle reçoit une valeur, elle met à jour la valeur commune. Elle scrute aussi le canal out et, peu importe la valeur reçue dans le canal, elle insère la valeur commune dans ce canal. L'instruction for sert ici à réaliser une boucle sans fin. Boucle durant laquelle on vérifie l'état des deux canaux. Note : lorsque l'on définit que le paramètre d'une fonction est de type chan, on peut définir sa direction (il est bidirectionnel par défaut). Indiquer la direction du canal renforce la sécurité de votre application par un typage plus strict (une erreur sera provoquée en cas d'utilisation bidirectionnelle).
func valeurManagerSansEtat(in<- chan string, out chan string, initValue string) { valeurCommune := initValue for { select { case valeurNouvelle := <-in: valeurCommune = valeurNouvelle case <-out: out <- valeurCommune } } }Le contrôleur handlerVoirValeur insère une valeur quelconque dans le canal bidirectionnel out et affiche la valeur lue en retour dans ce même canal. Le template utilisé est très simple (vous trouverez un exemple dans cet article).
func handlerVoirValeur(w http.ResponseWriter, r *http.Request, out chan string) { out <- "" valeur := <- out var p = Page{Title: "Voir la valeur", Valeur: valeur} err := templates.ExecuteTemplate(w, "voir.html", p) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }Le contrôleur handlerMajValeur récupère le paramètre txtValeur (GET ou POST) qu'il insère dans le canal dédié à la mise à jour de la valeur commune.
func handlerMajValeur(w http.ResponseWriter, r *http.Request, in chan<- string) { valeur := r.FormValue("txtValeur") in <- valeur http.Redirect(w, r, "/voir", http.StatusFound) }
Communication vers une routine avec état
package main import ( "net/http" "html/template" ) type Page struct { Title string Valeur string } type ValeurQuery struct { canalRetour chan string } var templates = template.Must(template.ParseFiles("voir.html")) func main() { maj := make(chan string) voir := make(chan ValeurQuery) go valeurManagerAvecEtat(maj, voir, "Init") http.HandleFunc("/voir", func(w http.ResponseWriter, r *http.Request) { handlerVoirValeur(w, r, voir) }) http.HandleFunc("/maj", func(w http.ResponseWriter, r *http.Request) { handlerMajValeur(w, r, maj) }) http.Handle("/",http.FileServer(http.Dir("."))) http.ListenAndServe(":9999", nil) }Comme expliqué précédemment, on applique un typage strict indiquant que le canal est unidirectionnel (c'est le cas pour les deux paramètres cette fois-ci). Le fonctionnement est le même que dans le code précédent sauf que pour retourner la valeur commune, on utilise le canal temporaire (demande.canalRetour) qui a été créé par l'appelant (le contrôleur handlerVoirValeur).
func valeurManagerAvecEtat(maj<- chan string, voir<- chan ValeurQuery, initValue string) { valeurCommune := initValue for { select { case valeurNouvelle := <-maj: valeurCommune = valeurNouvelle case demande := <-voir: demande.canalRetour <- valeurCommune } } }Le contrôleur handlerVoirValeur commence par construire un canal temporaire (dans lequel on lira la réponse de la goroutinevaleurManagerAvecEtat). On initialise la structure ValeurQuery avec ce canal et c'est une variable de type ValeurQuery qui est envoyée à la goroutine via le canal de demande. Contrairement au cas précédent, on peut fermer le canal de réponse après l'avoir reçue, car nous avons construit un canal spécifique à la demande qui ne sera plus utilisé.
func handlerVoirValeur(w http.ResponseWriter, r *http.Request, voir chan<- ValeurQuery) { reponse := make(chan string) voir <- ValeurQuery{reponse} valeur := <-reponse close(reponse) var p = Page{Title: "Voir la valeur", Valeur: valeur} err := templates.ExecuteTemplate(w, "voir.html", p) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }Le contrôleur handlerMajValeur est indentique au cas précédent (Je l'ai reproduit ici, mais ce n'est pas l'objet de ces paragraphes).
func handlerMajValeur(w http.ResponseWriter, r *http.Request, maj chan<- string) { valeur := r.FormValue("txtValeur") maj <- valeur http.Redirect(w, r, "/voir", http.StatusFound) }
Une traduction du blog officiel de golang expliquant le mécanisme de la réflexion en Go. Lire »
Traduction d'un article du blog officiel expliquant comment échanger des données entre deux programmes golang grâce à un format natif Lire »
Traduction d'une partie des spécifications officielles du langage go, cet article explique comment go gère la mémoire. Lire »
Préconisations officielles pour la gestion des erreurs dans un programme golang. Cet article complète les explications sur panic, defer et recover Lire »
La bibliothèque standard de go ne gère pas les variables de session d'une application. Il existe une solution avec le toolkit Gorilla Lire »
Soyez le premier à commenter cet article
Tous les commentaires sont soumis à modération. Les balises HTML sont pour la plupart autorisées. Les liens contextuels et intéressants sont en follow.