Maison >développement back-end >Golang >Comment les opérations de canal chaînées dans l'instruction « select » de Go peuvent-elles entraîner des blocages et des pertes de données ?
Chaînage des opérations de canal dans un cas de sélection unique : analyse d'un piège potentiel
Dans Go, l'instruction select fournit un moyen pratique de multiplexer plusieurs chaînes. Considérons le scénario suivant : nous avons deux canaux, A et B, transmettant des messages à des intervalles différents. Nous souhaitons créer une chaîne de fans qui reçoit des messages de A et de B.
Le code ci-dessous le démontre :
func fanIn(input1, input2 <-chan string) <-chan string { ch := make(chan string) go func() { for { select { case t := <-input1: ch <- t case t := <-input2: ch <- t } } }() return ch }
Lorsque nous exécutons ce code, nous nous attendons à recevoir des messages des deux canaux de manière entrelacée. Cependant, si nous modifions l'instruction select case comme suit, nous rencontrons un comportement inattendu :
func fanIn(input1, input2 <-chan string) <-chan string { ch := make(chan string) go func() { for { select { case ch <- <-input1: case ch <- <-input2: } } }() return ch }
Dans ce cas, nous recevons correctement certains messages, mais nous rencontrons ensuite des valeurs perdues et finalement un blocage. La raison de ce comportement réside dans le fonctionnement fondamental de select.
Dans select, une seule opération de lecture ou d'écriture de canal est non bloquante. Toutes les autres opérations se comportent normalement. Dans le code modifié, les deux cas contiennent des écritures de canal non bloquantes. Cela conduit à une situation dans laquelle les messages des canaux d'entrée sont mis en file d'attente, mais le canal de fan-in ne peut en consommer qu'un à la fois.
En conséquence, les messages peuvent être supprimés et un blocage se produit lorsque le fan-in le canal n'a pas d'écrivain et le lecteur attend plus de valeurs.
Pour éviter ce problème, il est crucial de comprendre que dans une instruction select, une seule opération doit être non bloquante. Si vous devez effectuer plusieurs opérations de canal dans un seul cas de sélection, envisagez d'utiliser une fonction d'assistance de sélection non bloquante comme celle-ci :
func nonBlockingSelect(cases []reflect.SelectCase) (chosen int, recv interface{}, ok bool) { for i, c := range cases { if c.Dir == reflect.SelectSend && c.Chan == nil { continue } v, ok := reflect.Select(cases) return v.Index, v.Elem().Interface(), ok } return -1, nil, false }
Ensuite, la fonction de fan-in modifiée peut être réécrite comme :
func fanIn(input1, input2 <-chan string) <-chan string { ch := make(chan string) go func() { for { select { case c1 := <-input1: nonBlockingSelect([]reflect.SelectCase{ {Dir: reflect.SelectSend, Chan: reflect.ValueOf(ch), Send: reflect.ValueOf(c1)}, }) case c2 := <-input2: nonBlockingSelect([]reflect.SelectCase{ {Dir: reflect.SelectSend, Chan: reflect.ValueOf(ch), Send: reflect.ValueOf(c2)}, }) } } }() return ch }
L'utilisation de l'assistant de sélection non bloquant garantit qu'une seule opération de canal est non bloquante, évitant ainsi le problème de valeurs perdues et de blocages.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!