2016-07-09 12 views
4

Ich versuche, dieses Problem auf Exercism zu lösen:Ärger mit goroutines in einer for-Schleife

Write a program that counts the frequency of letters in texts using parallel computation.

Grundsätzlich habe ich einen FreqMap Typ:

type FreqMap map[rune]int 

Und eine Frequency Funktion:

func Frequency(s string) FreqMap { 
    m := make(FreqMap) 
    for _, v := range s { 
     m[v]++ 
    } 
    return m 
} 

Exercism stellt ein Beispiel dar, das eine gleichzeitige Version implementiert Verwenden von Rekursion, aber ich möchte meine eigene Version mit einer for Schleife implementieren. Ich kam mit der folgenden Lösung auf, die nicht funktioniert:

nach nur 1 Iteration
func ConcurrentFrequency(l []string) FreqMap { 
    c := make(chan FreqMap) 
    for i := 0; i < len(l); i++ { 
     go func(i int) { 
      c <- Frequency(l[i]) 
     }(i) 
    } 
    return <- c 
} 

Dies scheint zurückzukehren, c das Ergebnis von nur 1 goroutine zu enthalten scheint; Ich bekomme das gleiche Ergebnis, wenn ich eine sync.WaitGroup hinzufüge.

Könnten Sie mir bitte erklären, was mir hier fehlt?

Vielen Dank im Voraus für Ihre Hilfe!

Antwort

4

Ihr Code scheint nur eine Iteration zu machen, weil ConcurrentFrequency den ersten Wert aus dem Kanal zurückgibt und das ist es. Ich quess Sie so etwas wie dies wollen:

func ConcurrentFrequency(l []string) chan FreqMap { 
    c := make(chan FreqMap) 
    go func() { 
     var wg sync.WaitGroup 
     wg.Add(len(l)) 
     for _, s := range l { 
      go func(s string) { 
       defer wg.Done() 
       c <- Frequency(s) 
      }(s) 
     } 
     wg.Wait() 
     close(c) 
    }() 
    return c 
} 

Jetzt ist es den Kanal von Karten gibt, werden diese Sie woanders vermutlich in Sigle Karte kombinieren:

func main() { 
    m := make(FreqMap) 
    for v := range ConcurrentFrequency([]string{"foo", "bar","zoo"}) { 
     for k, v := range v { 
      m[k] += v 
     } 
    } 
    fmt.Println(m) 
} 

längere Erklärung, die nicht der Fall ist passen in Kommentar:

In der for _, s := range l Schleife schreiben alle goroutines in den gleichen Kanal, aber als dieser Kanal ist nicht Puffer ed, sobald der erste Wert darin geschrieben ist, ist er "voll", was bedeutet, dass keine anderen Werte in ihn geschrieben werden können. So kann nur eine Goroutine in der Schleife abgeschlossen werden, und wg.Done wird nur einmal aufgerufen. Wenn also das Quell-Array mehr als eine Zeichenkette hat, kann der Rest der Gorutinen nicht abgeschlossen werden, bis etwas beginnt, Werte von dem Kanal zu verbrauchen. Aber in Ihrer Version würde es in wg.Wait steckend sein, da nicht alle goroutines getan sind und folglich der ConcurrentFrequency den Kanal zum Verbraucher nicht zurückbringen kann. In der Art, wie ich die ConcurrentFrequency schrieb, kann die Cannel zum Verbraucher zurückgegeben werden und dies (Lesen aus dem Kanal) ermöglicht die anderen Frequency(s) Anrufe in Kanal schreiben.

+0

Vielen Dank, das scheint mein Problem zu lösen! Könntest du mir bitte erklären, warum wir die erste Gorotine brauchten, die auch die 'for'-Schleife einwickelte? Wenn ich es entferne, gibt es einen "fatalen Fehler: alle Goroutines schlafen - Deadlock!", Aber ich sehe den Grund nicht. Danke noch einmal! –

+0

Nicht sicher, wie Sie den Code geändert haben, aber ich bezweifle, dass es bei der wg.Wait Deadlocks - die Verbraucherschleife wird dann nicht erreicht werden, da der Code dort stecken würde. Ich habe die WaitGroup verwendet, damit der Kanal geschlossen werden kann, wenn alle 'Frequency'-Aufrufe zurückkommen. Das ist das übliche Muster, wenn Sie den Kanal zurückgeben. – ain

+0

Also, wenn es keine Goroutine Schließung der ersten Ebene gibt, 'wg.Wait()' Timeouts den Rest? Mein Vorteil ist, dass 'wg.Done()' den WaitGroup-Zähler dekrementiert, sobald die Schließung der Goroutine in der 'for'loop-Schleife zurückkehrt; Warum gibt es dann einen Deadlock wenn 'wg.Wait() 'wird in der' ConcurrentFrequency() '- Ebene und nicht in einer verschachtelten Goroutine platziert? Entschuldige, dass ich dich noch einmal belästige, versuche nur das meiste davon zu verstehen und dein Feedback wird sehr geschätzt! –