Concurrence
La concurence en Go fait partie du language lui-ême. Ce n’est pas une bibliothèque.
La ysntaxe de Go permet de
- démarrer en tâche concruurente
- communiquer avec elle
- réagire à cette communication
Comme vu dans le Go Tour les primitives de base pour la concurrence sont les goroutines et les cannaux (channels).
Les goroutines sont des fonctions s’exécutant indépendamment dans un espace d’adressage commun
go f()
go g(1, 2)
Les canaux permettent aux goroutines de se synchroniser et d’échanger des messages.
c := make(chan int)
go func() { c <- 3 }()
n := <-c
Ping-Pong
L’exemple ci-dessous illustre lûtilisation de ces primitives :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
En faisant tourner ce programme, nous obtenons le résultat suivant :
ping 1
pong 2
ping 3
pong 4
ping 5
pong 6
ping 7
pong 8
ping 9
pong 10
ping 11
Notez que Go est pourvu d’un détecteur de deadlock et si vous commentez la ligne 15 qui lance la balle sur la table dans le programme précédent, vous obtenez l’erreur suivante au run-time :
fatal error: all goroutines are asleep - deadlock!
Une faiblesse du programme ci-dessus c’est que les deux goroutines qui implémentent les joueurs ne terminent jamais d’elle-même. Elle terminnent bien quand le programme principal termine, mais ce n’est pas très élégant.
Poiur adresses ce problème, nous utilisons la primitive select qui,
avec les goroutines et les cannaux est le troisième pilier de la
concurrence en Go.
SUpposons que vous souhaitons implémenter une gorooutine qui communique en recevant et en envoyant des messages. Le coeur de la goroutine ressemble à ça:
select {
case xc <- x:
// sent x on xc
case y := <-yc:
// received y from yc
}
Le select bloque jusqu’à ce qu’un des case puisse continuer et chaque
case est une communication. Dans cet exemple, le premier case essaye
d’envoyer la valeur x dans le cannal xc et et deuxième essaye
de reçcvoir une valeur du canal yc et la stocke dans la variable y.
Si la fonction ne peut ni écrire, ni lire, elle bloque. Sinon, un seul
des case est suivi. Ca veut dire que si y est assigné à la
valeur reçue par yc, nous savons que nous n’avons pas envoyé x
dans xc.
Lecteur de feed
Pour la suite de ce cours, nous construisons un lecteur de feed (feed reader) qui nous permet de consulter les dernières nouvelles du jour.
Pour implémenter le lecteur, nous avons besoin d’un client RSS.
Le package gofeed disponible sur https://pkg.go.dev/github.com/mmcdole/gofeed
nous offre des fonctions permettant de lire un flux RSS.
la fonction ParseURL retourne une instance de type Feed.
type Feed struct {
Title string
Description string
...
Items []*Item
...
}
L’attribut Items de Feed`` est un tableau de pointeurs vers les articles (Item`)
type Item struct {
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Content string `json:"content,omitempty"`
Link string `json:"link,omitempty"`
Links []string `json:"links,omitempty"`
...
}
Vous pouvez tester ce package avec le programme suivant :
package main
import (
"fmt"
"github.com/mmcdole/gofeed"
)
func main() {
fp := gofeed.NewParser()
feed, _ := fp.ParseURL("https://www.fr.ch/directions-services/71391/rss.xml")
fmt.Println("---", feed.Title, "---")
fmt.Println()
for _, item := range feed.Items {
fmt.Println(item.Title)
}
}
Supposons maintenant que nous ne souhaitons pas juste avoir un tableau d’article, mais un canal qui nous délivre les articles. Et même un canal capable de délivrer les articles à plusieurs consommateurs à la fois. Nous implémentons pour cela un modèle d’abonnement :
type Subscription interface {
Updates() <-chan gofeed.Item // stream of Items
Close() error // shuts down the stream
}
func Subscribe(parser gofeed.Feed) Subscription {...} // converts a Feed to a stream
func Merge(subs ...Subscription) Subscription {...} // merges several streams
La fonction Subscribe s’abonne au feed spécifié par le parser et retourne une
valeur de type Subscription. Une Subscription est une interface avec
une méthode Updates qui retourne un canal qui envoie des Items et
une méthode Close qui clos l’abonnement et termine l’envoi d`articles
De plus, Le fonction Merge permet de fusionner plusieurs abonnements en un seul.