简单的讲,在非协程环境 Channel 满了,就阻塞写,Channel空了,就阻塞读。
如果阻塞发生在 main 主进程里,并且没有其他子协程可以执行,那就可以确定“希望永远等不来”,main 协程会把自己杀掉,报一个 fatal error:deadlock 错误。
下面的代码将报错 :
fatal error: all goroutines are asleep - deadlock!
package main
func main() {
ch := make(chan int)
// 写入数据
ch <- 1
res := <-ch
println(res)
println("主进程结束")
}
原因是向管道添加数据时没有与取出同时执行,改进一下 :
package main
func main() {
ch := make(chan int, 1)
// 写入数据
ch <- 1
res := <-ch
println(res)
println("主进程结束")
}
创建一个带有缓冲区的管道,可以缓冲一个数据,这样就不在需要存取同步。
如果在取出之前再存入一条数据就会报错 :
package main
func main() {
ch := make(chan int, 1)
// 写入数据
ch <- 1
// 此处会造成阻塞
ch <- 2
res := <-ch
println(res)
println("主进程结束")
}
如果阻塞发生在子协程里,就不会发生死锁,因为至少 main 协程是一个值得等待的“希望”,会一直等下去。
go 的协程很聪明,阻塞之后它就主动交出 cpu,相当于调用 runtime.Gosched(),让其他协程去执行,希望其他协程能帮自己解除阻塞(当然是通过读写管道的方式)。
package main
func main() {
ch := make(chan int)
// 开启一个协程
go func() {
// 存入数据
// 此处会临时阻塞,等待有取出数据动作与填充动作匹配
ch <- 1
}()
// 取出数据
res := <-ch
println(res)
println("主进程结束")
}
读一个空管道或写一个缓冲已经满的管道,到底会发生什么行为,需要分情况讨论:
1. 发生在非 main 协程里,会阻塞。
2. 发生在 main 协程里 :
2.1 没有其他非 main 协程可以执行,报 fatal error: all goroutines are asleep - deadlock!
2.2 有其他非main协程可以执行,则 main 协程会让他们先执行。
2.2.1 非 main 协在程执行过程中,帮 main 协程解除了阻塞。
2.2.2 非 main 协执行结束后,依然没有帮 main 协程解除阻塞,则 main 协程报 fatal error: all goroutines are asleep - deadlock!