Introduction
- Nous écrivons Dolt, la première base de données SQL avec gestion de versions au monde, en langage Go
- Comme la plupart des codebases Go, nous utilisons des canaux et des goroutines pour implémenter l’exécution concurrente
- En règle générale, la programmation concurrente est difficile, donc nous utilisons des méthodes simples et intuitives
- Cependant, nous avons hérité dans un autre projet open source d’un code qui utilise les canaux d’une manière très originale
var c chan chan struct{}
- Cela consiste à faire passer des canaux entre différentes goroutines afin d’implémenter un motif de fan-out entre goroutines de travail
- Cette approche était difficile à comprendre et compliquée à manipuler lorsqu’on tient compte des fuites de goroutines
- Nous avons finalement réécrit ce code pour éliminer
chan chan struct{}
Pourquoi faire ça
- Il existe une vieille blague de programmation qui remonte à l’époque où le C et ses langages dérivés dominaient
- Beaucoup de gens avaient du mal à comprendre les pointeurs
- Comme Go est lui aussi un langage dérivé du C, il peut faire la même chose
func main() {
i := 1
setInt(&i)
fmt.Printf("i is now %d", i)
}
func setInt(i *int) {
setInt2(&i)
}
func setInt2(i **int) {
setInt3(&i)
}
func setInt3(i ***int) {
setInt4(&i)
}
func setInt4(i ****int) {
****i = 100
}
- Ce code compile et affiche
i is now 100
- On peut faire la même chose en Go avec des canaux
Le programmeur Go 4-chan
- Nous allons écrire un programme qui utilise 4 niveaux d’indirection de canaux
- Le canal de niveau supérieur est déclaré comme un 4-chan
_4chan := make(chan chan chan chan int)
- La valeur envoyée dans ce canal est un 3-chan
_3chan := make(chan chan chan int)
- À chaque niveau d’indirection, nous créons des producteurs selon un facteur de branchement constant
func sendChanChanChan(c chan chan chan chan int) {
for range factor {
go func() {
logrus.Debug("starting 3chan producer")
_3chan := make(chan chan chan int)
sendChanChan(c, _3chan)
}()
}
}
- Les consommateurs sont traités de la même manière
func receiveChanChanChan(c chan chan chan chan int) {
for _3chan := range c {
logrus.Debug("got message from 4chan")
for range factor {
logrus.Debug("starting 3chan consumer")
go receiveChanChan(_3chan)
}
}
}
- Finalement, on atteint l’étape où les vraies valeurs sont envoyées
func send(_2chan chan chan int, _1chan chan int) {
_2chan <- _1chan
for range factor {
go func() {
logrus.Debug("starting int producer")
for range factor {
go func() {
logrus.Debug("sending int")
_1chan <- 1
}()
}
}()
}
}
- Le consommateur additionne les valeurs reçues
var sum = &atomic.Int32{}
func receive(c chan int) {
for s := range c {
logrus.Debug("received int")
sum.Add(int32(s))
}
}
- En assemblant le tout, on l’exécute
const factor = 3
var sum = &atomic.Int32{}
func main() {
// logrus.SetLevel(logrus.DebugLevel)
_4chan := make(chan chan chan chan int)
go sendChanChanChan(_4chan)
go receiveChanChanChan(_4chan)
time.Sleep(500 * time.Millisecond)
fmt.Printf("%d ^ 5: %d", factor, sum.Load())
}
- Ce programme calcule la puissance 5 d’un nombre de la manière la plus distribuée possible
Commentaire
- Il existe de nombreuses raisons de ne pas faire cela dans du vrai code : difficulté d’implémentation et de débogage, question d’ego, reproches des collègues, etc.
- Mais c’est intéressant parce que c’est très amusant et que ça fonctionne
- L’une des raisons pratiques est qu’envoyer un canal dans un autre canal rend sa fermeture très difficile
Conclusion
- Si vous avez des questions ou des remarques sur des motifs de concurrence amusants en Go, vous pouvez discuter avec notre équipe et d’autres utilisateurs de Dolt sur Discord
Résumé GN⁺
- Cet article traite d’un motif de concurrence original utilisant des canaux en langage Go
- Son usage dans du vrai code est inefficace, mais le concept est intéressant
- Il montre comment les fonctionnalités de concurrence de Go peuvent être utilisées dans un projet comme Dolt
- Parmi les projets offrant des fonctionnalités similaires, on trouve PostgreSQL, MySQL, etc.
1 commentaires
Avis Hacker News
En tant que scientifique, quand je travaille avec des ingénieurs logiciels professionnels, une grande partie de ce qu’ils font m’échappe
J’ai envie de laisser un commentaire peu substantiel et de faible effort
Une vieille blague de programmation datant de l’époque où C et ses langages dérivés dominaient reste toujours valable
Ça rappelle la musique classique du Buena Vista Social Club
J’ai déjà utilisé le motif
chan chan Valueouchan struct{resp chan Value}dans certaines situationsLes canaux de canaux sont un motif courant, qui apparaît généralement sous la forme d’un champ de type canal dans une structure
type request struct { params, reply chan response }Un billet de blog opposé à l’utilisation des canaux pour implémenter un mécanisme de dispatch dynamique
Ça fait penser à « My favorite Erlang Program » de Joe Armstrong
En cliquant sur le lien, je m’attendais à autre chose
Utilisation d’une approche similaire dans du code LabVIEW pour recevoir des données de réponse asynchrones