因此,让我们看看你的来源真正发生了什么。你有两个 goroutines(有两个以上,但我们将专注于明确的),main
和readFromChannel
。
让我们看看什么readFromChannel
做:
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.
现主营:
adds to waitgroup
make a channel `c`
start a goroutine `readFromChannel`
sleep for 5 seconds
send 10 to channel `c`
call wait for waitgroup
现在,让我们去通过执行您的代码流,同时(你的代码可能/可能不会执行以此顺序每次都记住)
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#
现在你可以gue ss为什么你会陷入僵局。在进行中,无缓冲通道将阻止,直到通道的另一端发生某些事情,无论您是发送还是接收。因此c <- 10
会阻塞,直到从c
的另一端读取某些内容为止,但您为此所用的参数在2秒前已经退出画面。因此,c
永远是块,并且因为main
是最后一个goroutine,你会得到一个死锁。
如何预防?使用频道时,请确保每个send
频道的另一端总是有一个receive
。您也可以使用缓冲通道,但在上面的代码中,它不会是“正确的”解决方案。
这里是我的僵局的解决办法:
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
这是我在一段时间内阅读最有用的答案之一。只是为了确保我遵循:“修复”的作品,因为它保持接收器频道即使超时。所以'wg.Done()'(并终止''main'例程)只会在从'c'读入内容时才会发生,对吧? –
没错,但为了清理一下,它保持** goroutine **运行。 – AJPennster