Go通道行为是否受消息发送方式影响?两段代码执行差异答疑
Go通道常见问题解答
1. Go通道(channel)的行为是否会根据消息发送方式的不同而发生改变?
通道的核心行为由其是否带缓冲以及并发执行环境决定,和发送操作在哪个goroutine里执行无关。不管是主goroutine还是子goroutine发送数据,通道的阻塞规则是固定的:
- 无缓冲通道:发送操作必须等到有接收方准备好接收才能完成,接收操作也必须等到有发送方准备好发送才能完成,双方是同步交互的。
- 缓冲通道:只要缓冲队列没满,发送操作就能直接把数据存入队列而不阻塞;只要缓冲队列不为空,接收操作就能直接取出数据而不阻塞。
2. 两段Go代码执行结果不同的原因分析
代码①触发死锁的原因
这段代码创建的是无缓冲通道,在主goroutine里执行ch <- 1时,没有任何其他goroutine在等待接收这个数据,所以发送操作会一直阻塞主goroutine。而主goroutine被阻塞后,后续的fmt.Println(<-ch)根本没机会执行,整个程序里所有goroutine都陷入阻塞状态,Go运行时就会抛出fatal error: all goroutines are asleep - deadlock!错误。
如果把这段代码里的通道改成缓冲通道(比如make(chan int, 1)),缓冲队列能容纳一个元素,ch <- 1会直接把数据存入缓冲,不会阻塞主goroutine,后续的接收操作就能正常取出数据执行,程序也就不会死锁。
代码②能正常执行的原因
这段代码启动了一个子goroutine来执行buffer函数,主goroutine和子goroutine是并发运行的:
- 主goroutine执行第一个
fmt.Println(<-ch)时,会阻塞等待数据。 - 子goroutine执行
ch <- 2,此时主goroutine正在等待接收,发送操作完成,主goroutine收到数据后输出2。 - 主goroutine接着执行第二个
fmt.Println(<-ch),再次阻塞等待数据。 - 子goroutine执行
ch <- 3,此时主goroutine在等待接收,发送操作完成,主goroutine收到数据后输出3。
整个过程中,发送和接收操作是交叉同步完成的,没有出现所有goroutine都阻塞的情况,所以不会触发死锁。buffer函数里能发送两个值而不报错,是因为每次发送都有对应的接收操作在等待,发送操作不会一直阻塞,自然能顺利完成。
内容的提问来源于stack exchange,提问作者Girish Rao
相关产品推荐
相关产品推荐

