Golang中Goroutine和channel的使用指南
Golang中Goroutine和channel的使用指南
我需要一些帮助来理解通道。在下面的代码中,如果我将 court <- 1 移到两个 goroutine(go play…)之前,就会出现死锁。我知道无缓冲通道会阻塞,直到接收和发送同时准备就绪。但是当我把 court <- 1 移到两个 goroutine 之前时,接收部分就在这两个 goroutine 中,我想知道为什么会导致死锁?把 court <- 1 放在两个 goroutine 之前和之后有什么区别?谢谢
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
var (
wg sync.WaitGroup
)
func init(){
rand.Seed(time.Now().UnixNano())
}
func main(){
wg.Add(2)
court := make(chan int)
go play("James", court)
go play("Yun", court)
court <- 1
wg.Wait()
}
func play(name string, court chan int){
defer wg.Done()
for {
ball, ok := <- court
if !ok {
fmt.Printf("player %s won\n", name)
return
}
n := rand.Intn(100)
if n % 13 == 0{
fmt.Printf("Player %s missed\n", name)
close(court)
return
}
fmt.Printf("Player %s Hit %d\n", name, ball)
court <- ball + 1
}
}
更多关于Golang中Goroutine和channel的使用指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html
通过在之前放置,您需要等待通道上有内容发送后才启动 go 协程,这就是发生死锁的原因。
更多关于Golang中Goroutine和channel的使用指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你没问题 @NobbZ,我只是在尝试理解 goroutine 中的通道机制…
所以在我看来,无缓冲通道无论是发送数据还是接收数据,都需要在主函数执行前启动 goroutine。否则就会出现死锁。我的理解正确吗?
哦,等等,我完全误解了你的问题……
问题不在于你试图在没有数据可用时读取而导致阻塞,而在于你试图在无缓冲通道上发送数据时,实际上没有可用的接收者。
但基本上效果是一样的。死锁。
我想我该就此打住了,在说更多胡话之前,赶紧给自己找张床休息……
那么当我把这个放在 goroutine 之后,goroutine 中使用的通道也会阻塞该 goroutine,对吗?这些带通道的 goroutine 看起来简单,但实际上我觉得我还是不太明白如何使用它。
我应该在发送到通道之前先使用从通道接收的通道吗?还是相反的方式? 通道应该放在 goroutine 之后吗?
在Golang中,无缓冲通道的发送和接收操作必须同时准备好才能成功执行,否则会导致goroutine阻塞。在你的代码中,将 court <- 1 移到两个goroutine之前会导致死锁,原因如下:
死锁分析:
当 court <- 1 在goroutine启动之前执行时:
- 主goroutine尝试向无缓冲通道
court发送值1 - 此时没有其他goroutine在等待接收这个值(两个play goroutine尚未开始执行)
- 主goroutine被阻塞在发送操作上,无法继续执行
- 因此两个play goroutine永远不会被调度执行
- 形成了循环等待:主goroutine等待接收者,但接收者永远不会被创建
正确的执行顺序:
func main(){
wg.Add(2)
court := make(chan int)
go play("James", court)
go play("Yun", court)
court <- 1 // 在goroutine启动后发送
wg.Wait()
}
为什么这样能正常工作:
- 两个play goroutine先启动,进入循环等待接收通道数据
- 主goroutine发送
court <- 1时,已经有goroutine在等待接收 - 其中一个play goroutine接收到值后继续执行
- 程序正常进行乒乓球模拟
示例演示阻塞情况:
// 会导致死锁的版本
func main(){
court := make(chan int)
court <- 1 // 阻塞在这里,没有接收者
go func() {
<-court // 这个goroutine永远不会执行
}()
}
在你的代码中,play函数的设计要求通道两端都有goroutine准备就绪:一个发送,一个接收。将发送操作提前破坏了这种准备就绪的平衡,导致所有goroutine都无法继续执行。
这就是无缓冲通道的同步特性:发送和接收必须成对出现且同时准备好。

