Am besten Weg mit sync.WaitGroup mit externen Funktion
Ich habe einige Probleme mit dem folgenden code:
package main
import (
"fmt"
"sync"
)
//This program should go to 11, but sometimes it only prints 1 to 10.
func main() {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go Print(ch, wg) //
go func(){
for i := 1; i <= 11; i++ {
ch <- i
}
close(ch)
defer wg.Done()
}()
wg.Wait() //deadlock here
}
//Print prints all numbers sent on the channel.
//The function returns when the channel is closed.
func Print(ch <-chan int, wg sync.WaitGroup) {
for n := range ch { //reads from channel until it's closed
fmt.Println(n)
}
defer wg.Done()
}
Bekomme ich einen deadlock an der angegebenen Stelle. Ich habe versucht Einstellung wg.Add(1)
anstatt 2 und es löst mein problem. Meine überzeugung ist, dass ich nicht erfolgreich, sendet der Kanal als argument für die Printer
Funktion. Gibt es eine Möglichkeit, das zu tun? Sonst eine Lösung zu meinem problem ist das ersetzen der go Print(ch, wg)
Zeile mit:
go func() {
Print(ch)
defer wg.Done()
}
und die änderung der Printer
Funktion:
func Print(ch <-chan int) {
for n := range ch { //reads from channel until it's closed
fmt.Println(n)
}
}
Was ist die beste Lösung?
- aus golang.org/pkg/sync er stellt fest: "Werte mit den Typen in diesem Paket sollte nicht kopiert werden" Also, pass nur
sync.WaitGroup
durch pointer-Referenz.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Gut, zuerst Ihre eigentliche Fehler ist, dass du gibst die
Print
Methode eine Kopie dersync.WaitGroup
, so dass es nicht zu nennen, dieDone()
Methode, nach der manWait()
ing auf.Versuchen Sie dies:
Nun, wenn Sie Ihre
Print
Methode zum entfernen derWaitGroup
es ist generell eine gute Idee: die Methode braucht nicht zu wissen, etwas abzuwarten, bis er seine Arbeit fertig zu stellen.Print
statt derWaitGroup
selbst. Ich bin damit einverstanden, dass die Methode braucht nicht zu wissen über dieWaitGroup
, aber angenommen, ich möchte das auf jeden Fall. Und was hat der Stern zu tun? Es tut pick, die die TATSÄCHLICHENWaitGrooup
ich habe inmain
? Und warum ist diese nicht eine Kopie?*sync.WaitGroup
sagt dem compiler, den Sie wollen, einen Zeiger auf eineWaitGroup
statt einerWaitGroup
. Damit der compiler erwartet, dass Sie die Methode aufrufen, indem Sie ihm eine Adresse, damit die&wg
.Print
ist verpackt in einem anonymen goroutine inmain
undwg.Done()
ist aufgerufen, zu diesem Anwendungsbereich.Stimme ich mit @Elwinar die Lösung, dass das größte problem in Ihrem code verursacht wird, indem eine Kopie Ihrer
Waitgroup
zu denPrint
Funktion.Dies bedeutet, dass die
wg.Done()
betrieben wird, auf einer Kopie deswg
Sie in dermain
. Daherwg
immain
konnte nicht verringert, und somit ein deadlock passiert, wenn Siewg.Wait()
im main.Da Ihnen auch Fragen über die beste Praxis, ich konnte Ihnen einige Anregungen für meine eigene:
Nicht entfernen
defer wg.Done()
imPrint
. Da Ihr goroutine in main ist ein sender, und drucken ist ein Empfänger, entfernenwg.Done()
in der Empfänger routine verursachen eine unvollendete Empfänger. Dies ist, weil nur Ihr sender ist synchronisiert mit Ihrem Haupt, so nach und nach Ihre sender geschehen ist, Ihr Haupt ist getan, aber es ist möglich, dass der Empfänger noch funktioniert. Mein Punkt ist: nicht verlassen, einige baumelten goroutines um nach deiner main-routine beendet ist. Schließen Sie Sie, oder warten Sie für Sie.Daran denken dies zu tun panic recovery überall, vor allem anonyme goroutine. Ich habe gesehen, dass viele golang Programmierer vergessen zu setzen Panik Erholung in goroutines, auch wenn Sie nicht vergessen, wieder in die normalen Funktionen. Es ist kritisch, wenn Sie wollen, dass Ihr code korrekt Verhalten oder zumindest würdevoll, wenn etwas unerwartetes passiert.
Verwenden
defer
vor jeder kritische Anrufe, wiesync
Verwandte Anrufe, am Anfang, da Sie nicht wissen, wo Sie den code brechen kann. Lassen Sie uns sagen, dass Sie entferntdefer
vorwg.Done()
, und einen Panik-Auftritt in Ihrer anonymen goroutine in deinem Beispiel. Wenn Sie nicht haben Panik erholt, wird es Panik. Aber was passiert, wenn Sie eine Panik zu erholen? Alles ist in Ordnung jetzt? Nein. Sie erhalten deadlock beiwg.Wait()
seit Ihrwg.Done()
wird übersprungen, weil der Panik! Aber mitdefer
diesewg.Done()
ausgeführt werden, die am Ende, auch wenn Panik passiert. Auch, aufschieben, bevorclose
ist auch wichtig, da Ihr Ergebnis wirkt sich auch auf die Kommunikation.So, hier ist der code modifiziert nach den Punkten, die ich oben erwähnt:
Hoffe es hilft 🙂