大家好,这是我的代码:
func main () {
// synChan := make(chan struct{}, 1)
intChan := make(chan int, 1)
ticker := time.NewTicker(time.Second)
go func() {
for range ticker.C {
select {
case intChan <- 1:
case intChan <- 2:
case intChan <- 3:
}
}
// synChan<- struct{}{}
fmt.Println("End [SEND]")
}()
var sum int
for e := range intChan {
fmt.Printf("接收到 %d\n", e)
sum += e
if sum > 3 {
fmt.Println(sum)
break
}
}
fmt.Println("End [REC]")
// <-synChan
}
这是《 GO 并发编程》里讲通道的一个题目,但是我试了几个方法之后会成死锁,请问大家如何打印出这个 fmt.Println("End [SEND]")。这里为什么不能打印出来?我想过是 main 函数退出了,所以试着使用通道阻塞,但直接会出死锁。。。
1
sexoutsex2011 263 天前
我蛮帮你问了大模型,你试试看:
这段 Go 代码的主要问题是,它在等待一个信号来表明发送 goroutine 已经结束,但是这个信号从未被发送。当`sum`大于 3 时,接收循环会中断,但是发送 goroutine 会因为`intChan`的缓冲区已满而阻塞,导致无法到达打印`"End [SEND]"`的代码行。 要解决这个问题,你需要确保发送 goroutine 有机会发送完成信号。在这个场景中,你可以通过关闭`intChan`来实现,因为关闭一个通道会导致所有接收者收到一个零值,并且随后的接收都会立即返回零值,而不会阻塞。因为`intChan`是一个 int 类型的通道,接收者会得到一个`0`。 以下是修正后的代码,它确保了打印出`"End [SEND]"`: ```go package main import ( "fmt" "time" ) func main() { intChan := make(chan int, 1) ticker := time.NewTicker(time.Second) done := make(chan struct{}) // 添加一个完成通道 go func() { for range ticker.C { select { case intChan <- 1: case intChan <- 2: case intChan <- 3: } } ticker.Stop() // 停止 ticker close(intChan) // 关闭 intChan fmt.Println("End [SEND]") close(done) // 发送完成信号 }() var sum int for e := range intChan { fmt.Printf("接收到 %d\n", e) sum += e if sum > 3 { fmt.Println(sum) break } } fmt.Println("End [REC]") <-done // 等待发送 goroutine 完成 } ``` 这里做了以下改动: 1. 添加了一个`done`通道,用来接收发送 goroutine 完成的信号。 2. `ticker.Stop()`被调用来停止 ticker ,避免资源泄露。 3. 在发送循环结束后关闭`intChan`通道。 4. 打印`"End [SEND]"`后,关闭`done`通道来发送一个完成信号。 5. 在主函数的最后,添加`<-done`来等待发送 goroutine 完成。 现在程序会等待发送 goroutine 结束后,才结束`main`函数,确保`"End [SEND]"`被打印出来。请注意,这个版本的代码假设发送 goroutine 总是会结束,否则可能会导致死锁。在实际编程中,你可能需要添加超时或其他机制来处理这种情况。 |
2
AoEiuV020JP 263 天前
@sexoutsex2011 #1 这个 ai 的答案你试过没有,这里是禁止搬运 ai 回答的,
|
3
liaowb3 OP @sexoutsex2011 不行,死锁了
|
4
Rehtt 263 天前
time.NewTicker 是创建了一个周期定时器,下面没有调用 ticker.Stop()来终止定时器,所以 for range ticker.C 回一直循环不会退出
|
5
PTLin 263 天前
你这个不叫死锁好吧,你要在适当的时候调用 ticker.Stop()让这个 Ticker 停下来,让 channel 被 close
|
6
Kisesy 263 天前
你接收完数据了,不向发送方发送结束命令,它还在发数据呢
|
7
Great233 263 天前
ticker.Stop() 停止计时器后只是不再发送数据,但是 channel 并没有被关闭
https://pkg.go.dev/time#Ticker.Stop ``` func main() { intChan := make(chan int, 1) ticker := time.NewTicker(time.Second) end := make(chan int) go func() { loop: for range ticker.C { select { case intChan <- 1: case intChan <- 2: case intChan <- 3: case <-end: fmt.Println("End [SEND]") ticker.Stop() break loop } } }() var sum int for e := range intChan { fmt.Printf("接收到 %d\n", e) sum += e if sum > 3 { fmt.Println(sum) end <- 1 break } } fmt.Println("End [REC]") } ``` |
9
sexoutsex2011 262 天前
@AoEiuV020JP 抱歉抱歉
|