3 回答
TA贡献1775条经验 获得超11个赞
所以,让我们看看你的源代码中到底发生了什么。你有两个goroutine(不止两个,但我们将专注于显式的),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#
现在你可以猜到为什么会出现死锁。在进行中,非缓冲通道将阻塞,直到通道的另一端发生某些事情,无论您是发送还是接收。所以c <- 10会阻塞,直到从 的另一端读取某些内容c,但是您为此拥有的 goroutine 已在 2 秒前从图片中删除。因此,c永远阻塞,并且由于main是最后一个 goroutine,你会得到一个死锁。
如何预防?使用频道时,请确保receive在频道的另一端始终有一个send.
在这种情况下使用缓冲通道可以作为一种快速修复,但可能会在更大的存储库中引发潜在的陷阱。例如,假设您c随后写入了更多数据并go readFromChannel(c, time.After(time.Duration(2)*time.Second))再次运行。你可能会看到:
Read D1
Read D2
或者
TIMED OUT
Read D1
完全基于机会。这可能不是您想要的行为。
以下是我解决死锁的方法:
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()
}
TA贡献1848条经验 获得超10个赞
你有一个无缓冲的频道。根据文档:
如果通道没有缓冲,发送方会阻塞,直到接收方收到该值。如果通道有缓冲区,发送方只会阻塞,直到值被复制到缓冲区
通过将通道更改为缓冲,我们可以避免死锁。
c := make(chan int, 10) // holds 10 ints
我还建议阅读https://golang.org/doc/effective_go.html#channels,里面有一些与频道相关的好东西。
TA贡献1802条经验 获得超10个赞
这是一个较老的问题,但我自己正在深入学习渠道并在这里找到了这个。
我认为您只需要在完成发送后关闭频道吗?
代码:
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
close(c) // <- CLOSE IT HERE
wg.Wait()
}
- 3 回答
- 0 关注
- 221 浏览
添加回答
举报
