Also schauen wir uns an, was wirklich in Ihrer Quelle passiert. Sie haben zwei goroutines (es gibt mehr als zwei, aber wir werden auf die expliziten konzentrieren), main
und readFromChannel
.
Schauen wir uns an, was readFromChannel
tut:
if channel `c` is not empty before `ti` has expired, print its contents and return, after signalling its completion to wait group.
if `ti` has expired before `c` is not empty, print "TIMED OUT" and return, after signalling its completion to wait group.
jetzt Main:
adds to waitgroup
make a channel `c`
start a goroutine `readFromChannel`
sleep for 5 seconds
send 10 to channel `c`
call wait for waitgroup
Jetzt lässt, für den Code der Ablauf der Ausführung durchlaufen, gleichzeitig (Code kann/ausführen kann nicht in dieser Reihenfolge jedes Mal, bedenken Sie das)
1) wg.Add(1)
2) c := make(chan int)
3) go readFromChannel(c, time.After(time.Duration(2)*time.Second))
#timer ti starts#
4) time.Sleep(time.Duration(5) * time.Second)
#MAIN Goroutine begins sleep
#timer ti expires#
5) case <-ti:
6) fmt.Println("TIMED OUT")
7) wg.Done()
# readFromChannel Goroutine returns #
#MAIN Goroutine exits sleep#
8) c<-10
9) ......#DEADLOCK#
Jetzt können Sie gue Warum hast du eine Sackgasse? In nicht blockierten Kanälen blockiert, bis etwas am anderen Ende des Kanals passiert, unabhängig davon, ob Sie senden oder empfangen. So c <- 10
wird blockieren, bis etwas vom anderen Ende von c
liest, aber die Goroutine, die Sie dafür hatten, ist aus dem Bild vor 2 Sekunden herausgefallen. Daher c
blockiert für immer, und seit main
ist die letzte goroutine links, erhalten Sie einen Deadlock.
Wie kann ich das verhindern? Stellen Sie bei der Verwendung von Kanälen sicher, dass sich am anderen Ende des Kanals immer ein receive
für jede send
befindet. Sie können auch einen gepufferten Kanal verwenden, aber in Ihrem obigen Code wäre es nicht die "richtige" Lösung.
Hier ist meine Lösung für das Deadlock:
func main() {
wg.Add(1)
c := make(chan int)
go readFromChannel(c, time.After(time.Duration(2)*time.Second))
time.Sleep(time.Duration(5) * time.Second)
c <- 10
wg.Wait()
}
func readFromChannel(c chan int, ti <-chan time.Time) {
// the forloop will run forever
loop: // **
for {
select {
case x := <-c:
fmt.Println("Read", x)
break loop // breaks out of the for loop and the select **
case <-ti:
fmt.Println("TIMED OUT")
}
}
wg.Done()
}
** see this answer for details
Dies ist eines der nützlichsten Antworten, die ich in einiger Zeit gelesen habe. Nur um sicherzugehen, dass ich folge: das "Fix" funktioniert, weil es den Empfängerkanal auch nach einer Auszeit hält. Also 'wg.Done()' (und das 'main' go routine beenden) wird immer nur passieren, wenn etwas von' c' eingelesen wurde, oder? –
Richtig, aber um etwas aufzuräumen, hält es die ** goroutine ** am Laufen. – AJPennster