2017-02-07 3 views
0

Ich bekomme "fataler Fehler: alle Goroutinen sind eingeschlafen - Deadlock! " aus irgendeinem Grund in der folgenden Code. Ich verwende einen gepufferten Kanal, der nicht blockierend sein sollte. Nicht sicher, was ich tue, falschKanal-Deadlock in Go

package main 

import (
    "fmt" 
    "sync" 
) 

func main() { 
    c := make(chan int, 2) 
    var wg sync.WaitGroup 
    wg.Add(2) 

    go doSomething(c, wg) 
    go doSomething(c, wg) 
    go doSomething(c, wg) 

    wg.Wait() 

    close(c) 

    for v := range c { 
     fmt.Print(v) 
    } 

} 

func doSomething(c chan<- int, wg sync.WaitGroup) { 
    defer wg.Done() 
    c <- 1 

} 

Spielplatz Link https://play.golang.org/p/J9meD5aKna

Antwort

1

Während Ihre Lösung funktioniert, bin ich nicht zufrieden damit.

Erstens, die Tatsache, dass Sie die Kanalgröße ändern müssen, damit es funktioniert, zeigt an, dass ein potenzielles Problem/Fehler vorliegt. Nun müssen Sie jedes Mal, wenn Sie einen weiteren doSomething starten wollen, daran denken, die Länge des Kanals zu ändern.

Zweitens warten Sie, bis alle goroutines beendet sind, bevor Sie vom Kanal lesen. Dies ist eine Art "Verschwendung", da einer der Hauptpunkte der Bereichsschleife über einen Kanal darin besteht, dass Sie nicht warten müssen, bis alle Elemente generiert sind (in den Kanal geschrieben), Sie können die Elemente sofort verarbeiten von ihnen sind bereit.

So würde ich Ihren Code als so etwas wie

func main() { 
    c := make(chan int) 

    var wg sync.WaitGroup 
    wg.Add(3) 
    go func() { 
     doSomething(c) 
     defer wg.Done() 
    }() 
    go func() { 
     doSomething(c) 
     defer wg.Done() 
    }() 
    go func() { 
     doSomething(c) 
     defer wg.Done() 
    }() 

    go func() { 
     wg.Wait() 
     defer close(c) 
    }() 

    for v := range c { 
     fmt.Print(v) 
    } 
} 

func doSomething(c chan<- int) { 
    c <- 1 
} 

https://play.golang.org/p/T3dfiztKot

Hinweis schreiben, wie das Warten und den Kanal zu schließen ist jetzt in einem eigenen goroutine - das Iterieren über den Kanal starten können (die ist jetzt ungepuffert!) sofort.

Ich änderte auch den Code, so dass die WaitGroup nie den Bereich, in dem es erklärt wird (dh es wird nicht als Parameter verwendet), das ist meine persönliche Vorliebe. Ich glaube, es macht den Code einfacher zu folgen und zu verstehen.

+0

Ich dachte, Sie können nicht über den Kanal reichen, bis der Kanal geschlossen ist. Vielleicht irre ich mich, aber das ist, was es hier sagt http://guzalexander.com/2013/12/06/golang-channel-tutorial.html "Wie oben erwähnt, wird der Bereich funktionieren, bis der Kanal explizit geschlossen wird".In diesem Fall müssen wir darauf warten, dass die andere Goroutine trotzdem ausgeführt wird. – tabiul

+0

Sie missverstanden - der Punkt ist, dass die Schleife nicht enden würde, bis der Kanal geschlossen ist. Wenn der Kanal also nie geschlossen wird, hat man eine Endlosschleife (es würde auf das nächste Element oder das Schließen des Kanals warten). – ain

+0

oh ok, du meinst also der for-Bereich wird blockieren bis der Kanal geschlossen ist. Ich denke dann ist deine Lösung besser – tabiul

0

ich das Problem gefunden. Tatsächlich zwei Probleme

  1. Die Größe des Kanals und wg sein sollte 3

  2. I die wg als Zeiger übergeben sollte

aktualisierten Code

package main 

import (
    "fmt" 
    "sync" 
) 

func main() { 
    c := make(chan int, 3) 
    var wg sync.WaitGroup 
    wg.Add(3) 

    go doSomething(c, &wg) 
    go doSomething(c, &wg) 
    go doSomething(c, &wg) 

    wg.Wait() 

    close(c) 

    for v := range c { 
     fmt.Print(v) 
    } 

} 

func doSomething(c chan<- int, wg *sync.WaitGroup) { 
    defer wg.Done() 
    c <- 1 

} 
0

Ja, Es gibt eine wichtige Probleme in Ihrem Code.

Sie rufen go doSomething(c, wg) einfach wg Wert übergeben. sollten Sie wissen,

Arguments in Go are always passed by value. Use a pointer when an argument may be modified.

so sollten Sie dieses go doSomething(c, &wg) tun, dann wg in main Funktion wird durch defer wg.Done() in func doSomething(c chan<- int, wg sync.WaitGroup) modifiziert werden.

.