ProgrammationDéveloppeur Go

Quelle est la différence entre un canal tamponné et un canal non tamponné en Go, et comment choisir la bonne option en fonction des tâches ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

En Go, un canal est un moyen de transmission de données entre des goroutines avec synchronisation. On distingue :

  • Canal non tamponné — bloque l'expéditeur jusqu'à ce que les données soient reçues dans une autre goroutine.
  • Canal tamponné — contient un tampon interne de taille fixe. L'expéditeur est bloqué uniquement si le tampon est plein.

Utiliser des canaux non tamponnés est pratique là où la synchronisation est critique, et il est nécessaire de garantir que l'expéditeur et le destinataire se rencontrent au même endroit dans le code. Les canaux tamponnés conviennent pour la transmission de données "avec réserve" — pour organiser des files d'attente, des travailleurs, des tâches asynchrones.

Exemple :

func main() { ch := make(chan int) // canal non tamponné go func() { ch <- 42 // se bloque tant qu'il n'y a pas de récepteur }() fmt.Println(<-ch) bufch := make(chan int, 2) // canal tamponné de 2 éléments bufch <- 1 // ne se bloque pas bufch <- 2 // ne se bloque pas // le prochain envoi se bloquera s'il n'y a pas de lecture }

Question piégée

Un canal tamponné peut-il servir de remplacement à une file d'attente entre des dizaines de producteurs et de consommateurs sans synchronisation supplémentaire ?

On répond souvent — « oui ». En réalité, seulement si l'ordre n'est pas important et que les messages ne sont pas perdus. Avec plusieurs lecteurs/écrivains, des situations de concurrence et de perte de données peuvent se produire, donc un contrôle de synchronisation est toujours nécessaire (piscine de travailleurs, mutex, contextes pour fermer les canaux).

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet


Histoire

Dans le traitement des logs, les programmeurs utilisaient un canal tamponné de longueur 1000 pour transmettre des événements entre le parseur et l'agrégateur. Lorsqu'ils ont arrêté le service, ils ont oublié de fermer le canal, et une partie des événements a été "perdue" — les travailleurs se sont terminés avant que tout le tampon ne soit traité. Cela a été corrigé par la fermeture explicite des canaux et un verrouillage jusqu'à ce que tout soit complètement vidé.


Histoire

En essayant de remplacer une file d'attente utilisant un mutex par un canal tamponné dans un service distribué, ils n'ont pas pris en compte les situations d'expéditeurs "bloqués" : avec un tampon plein, le blocage de l'envoi a finalement ralenti tout le système, provoquant des délais d'attente, et l'interface a commencé à "laguer". Après analyse, une partie de la synchronisation a été réintroduite via des variables de condition.


Histoire

Dans un module de télémétrie, un canal non tamponné était utilisé pour le journal des erreurs entre le module et le consommateur. En raison d'un retard aléatoire de traitement, l'écrivain était bloqué, et en une minute, une file d'attente de centaines de milliers de données s'est accumulée dans les goroutines — entraînant un "out of goroutines". Cela a été corrigé en remplaçant le canal non tamponné par un canal tamponné et un travailleur asynchrone pour nettoyer la file d'attente.