2017-10-06 13 views
-2

J'ai besoin d'exécuter une fonction plusieurs fois en parallèle.
Si même une fois que les fonctions retourne true (envoie true sur le canal) alors le résultat final devrait être true.Coincé avec des canaux dans golang

Comment réaliser cela en utilisant des goroutines et des canaux?

// Some performance intensive function 
func foo(i int, c chan bool) { 
    // do some processing and return either true or false 
    c <- true // or false 
} 

func main() { 
    flg := false 
    ch := make(chan bool) 
    for i := 0; i < 10; i++ { 
     go foo(i, ch) 
    } 
    // If even once foo() returned true then val should be true 
    flg = flg || <-ch 
} 
+0

"Comment réaliser cela en utilisant des canaux?" --- Pour quelle raison devez-vous utiliser des canaux pour cela? – zerkms

+1

S'il vous plaît nous montrer votre tentative. Votre code n'utilise aucun canal. – Flimzy

+0

(aussi, il est conseillé d'utiliser 'gofmt', car votre code n'est pas facile à lire comme écrit) – Flimzy

Répondre

0

Vous ne recevez qu'une seule valeur du canal (qui sera la valeur envoyée par l'un des foo() appels, imprévisible qui des nombreux) , mais vous voulez tout recevoir.

Il faut donc utiliser une boucle for pour recevoir autant de valeurs que vous envoyez (envoyé) à ce sujet:

for i := 0; i < 10; i++ { 
    flg = flg || <-ch 
} 

Bien que dans votre cas, il serait suffisant pour boucle jusqu'à une valeur true est reçue, car cela déterminez la valeur finale de flg, mais il est toujours recommandé de recevoir toutes les valeurs sinon les goroutines restantes seront bloquées (car ch est un canal non tamponné). Dans cet exemple, cela n'a pas d'importance, mais dans une application "réelle", les goroutines seraient bloquées pour toujours (fuite de mémoire).

Si vous ne voulez pas attendre que tous les appels foo() à remplir et retourner le plus tôt possible (dès une valeur true est rencontrée), une option est de faire ch tamponnées, de sorte que tous les goroutines peuvent envoyer des valeurs sur sans être bloqué.Et cette façon, vous n'êtes pas obligé de recevoir (et donc attendre) tous les appels foo() à compléter:

ch := make(chan bool, 10) 
for i := 0; i < 10; i++ { 
    go foo(i, ch) 
} 

flg := false 
for i := 0; i < 10; i++ { 
    if <-ch { 
     flg = true 
     break 
    } 
} 

Le choix de cette approche, vous devez fournir des moyens d'annuler goroutines dont le travail est plus nécessaire pour éviter CPU inutiles (et mémoire). context.Context est un tel moyen, en savoir plus à ce sujet ici: Close multiple goroutine if an error occurs in one in go.

1

Vous pouvez commencer la lecture du canal ch et mis flg à true une fois que vous obtenez vrai résultat. Comme ceci:

//flg = flg || <- ch 
for res := range ch { 
    if res { 
     flg = true 
    } 
} 

Cela fonctionne ainsi, mais a un grave inconvénient - boucle for attend de nouvelles valeurs infiniment du canal. La manière idiomatique d'arrêter la boucle est de fermer le canal. Vous pouvez le faire ainsi: exécuter un goroutine séparé qui attendra jusqu'à ce que tous les goroutins sortent. Go fournit un outil très pratique pour le faire - sync.WaitGroup.

Définir dans une portée globale de sorte que chaque goroutine peut accéder à elle:

var (
    wg sync.WaitGroup 
) 

Alors chaque fois que vous lancez goroutine vous ajoutez un plus goroutine attendre groupe:

for i := 0; i < 10; i++ { 
    wg.Add(1) // here 
    go foo(i, ch) 
} 

Quand se termine goroutine il appelle la méthode wg.Done pour le marquer. Puis la seporate goroutine attend jusqu'à ce que toutes les fours soient sortis et ferment le canal. wg.Wait blocs jusqu'à ce que tous sont faits:

go func() { 
    wg.Wait() 
    close(ch) 
}() 

Tous ensemble: https://play.golang.org/p/8qiuA29-jv