2016-05-25 7 views
5

Ich habe in den letzten Tagen versucht, einen Wechsel bei der Parallelität in Golang zu machen, indem ich eines meiner Kommandozeilenprogramme umgestaltet habe, aber ich stecke fest.Warum schreibt mein Golang-Kanal immer für immer?

Here's der ursprüngliche Code (Master-Zweig).

Here's der Zweig mit Nebenläufigkeit (Zweig x_concurrent).

Wenn ich den gleichzeitigen Code mit go run jira_open_comment_emailer.go ausführen, führt die defer wg.Done() nie, wenn die JIRA Frage zum here Kanal hinzugefügt wird, die meine wg.Wait() verursacht immer hängen.

Die Idee ist, dass ich eine große Anzahl von JIRA-Problemen habe, und ich möchte für jedes eine Goroutine ausgliedern, um zu sehen, ob es einen Kommentar hat, auf den ich antworten muss. Wenn dies der Fall ist, möchte ich es zu einer Struktur hinzufügen (ich habe nach einigen Nachforschungen einen Kanal gewählt), die ich später aus einer Warteschlange lesen kann, um eine Erinnerung per E-Mail zu erstellen.

Hier ist der entsprechende Abschnitt des Codes:

// Given an issue, determine if it has an open comment 
// Returns true if there is an open comment on the issue, otherwise false 
func getAndProcessComments(issue Issue, channel chan<- Issue, wg *sync.WaitGroup) { 
    // Decrement the wait counter when the function returns 
    defer wg.Done() 

    needsReply := false 

    // Loop over the comments in the issue 
    for _, comment := range issue.Fields.Comment.Comments { 
     commentMatched, err := regexp.MatchString("~"+config.JIRAUsername, comment.Body) 
     checkError("Failed to regex match against comment body", err) 

     if commentMatched { 
      needsReply = true 
     } 

     if comment.Author.Name == config.JIRAUsername { 
      needsReply = false 
     } 
    } 

    // Only add the issue to the channel if it needs a reply 
    if needsReply == true { 
     // This never allows the defered wg.Done() to execute? 
     channel <- issue 
    } 
} 

func main() { 
    start := time.Now() 

    // This retrieves all issues in a search from JIRA 
    allIssues := getFullIssueList() 

    // Initialize a wait group 
    var wg sync.WaitGroup 

    // Set the number of waits to the number of issues to process 
    wg.Add(len(allIssues)) 

    // Create a channel to store issues that need a reply 
    channel := make(chan Issue) 

    for _, issue := range allIssues { 
     go getAndProcessComments(issue, channel, &wg) 
    } 

    // Block until all of my goroutines have processed their issues. 
    wg.Wait() 

    // Only send an email if the channel has one or more issues 
    if len(channel) > 0 { 
     sendEmail(channel) 
    } 

    fmt.Printf("Script ran in %s", time.Since(start)) 
} 
+3

Sie haben 'len (channel)' überall, aber dieser Kanal hat keine Länge, weil er nicht gepuffert ist. Sie müssen vom Kanal empfangen, damit alle Sends abgeschlossen werden können (und im Allgemeinen ist das Treffen von Entscheidungen basierend auf der Länge eines gepufferten Kanals ein Fehler, da konkurrierende Operationen um diesen Wert rennen können) – JimB

+0

Also, wenn ich alles mache von mir schreibt in den Kanal, wartet auf sie zu vervollständigen, und dann aus dem Kanal lesen ... das kann nie passieren, weil die Sends nie wirklich abgeschlossen werden und die 'defer wg.Done()' auslösen? Wie würden Sie die Implementierung dieser Nebenläufigkeit im Allgemeinen in Angriff nehmen? Auch bin ich nicht sicher, dass Sie auf dem 'len (channel)' richtig sind, da die godocs angeben, dass es die aktuelle Anzahl der Elemente im Kanal zurückgibt, nicht die Kapazität wie 'cap (channel)' . https://golang.org/pkg/builtin/#len –

+0

'len (channel)' gibt die aktuelle Anzahl der Elemente in einem "gepufferten" Kanal zurück, aber da Kanäle normalerweise gleichzeitig verwendet werden, ist das Ergebnis von 'len' ist "abgestanden", sobald Sie es gelesen haben. Man würde im allgemeinen gleichzeitige gououtines haben, die vom Kanal senden und empfangen. Ich würde empfehlen, den Abschnitt [Concurrency] (https://tour.golang.org/concurrency/1) im Tour of Go erneut zu durchlaufen, um besser zu verstehen, wie Kanäle funktionieren. – JimB

Antwort

8

Die goroutines zum ungepufferte Kanal zum Senden blockieren. eine minimale Änderung der goroutines entblockt ist eine gepufferte Kanal mit einer Kapazität für alle Probleme zu schaffen:

channel := make(chan Issue, len(allIssues)) 

und den Kanal nach dem Aufruf von wg.Wait() schließen.

+2

Aber es vereitelt irgendwie den Zweck eines Kanals als ein Rohr zwischen konkurrierenden Blöcken .... – RickyA

+0

@RickyA Es ist nichts falsch mit der Verwendung eines Kanals ist eine Warteschlange von Elementen. –

+0

wahr, es erspart Ihnen den Overhead eines mutexed Slice herum. – RickyA