相信大家在从channel中读取数据的时候,有时候会遇到dead lock的情况,也就是死锁的情况,那这种情况一般是怎么出现的?又该怎么解决呢?下面我们来讨论一下。
出现的情况和解决方法
- 1 当一个channel中没有数据,而直接读取的时候,会发生死锁:
package main
import (
"fmt"
)
func main() {
channel := make(chan int)
x := <-channel
fmt.Println(x)
}
那么以上的代码就会报:fatal error: all goroutines are asleep - deadlock! 错误!
那如果我们通过新建一个协程,向其中添加数据,结果呢?
package main
import (
"fmt"
)
func main() {
channel := make(chan int)
go func(channel chan int){
channel<-1
}(channel)
x := <-channel
fmt.Println(x)
}
结果:1
程序会正常输出1,所以这种情况下就是正常的,所以对于这种无缓冲区的channel,我们必须给其中添加值,不然主协程就会阻塞,而主协程阻塞,就会导致dead lock现象发生。
- 2 过度写入数据造成的死锁
package main
import (
"fmt"
)
func main() {
channel := make(chan int, 2)
channel <- 1
channel <- 2
channel <- 3 //会导致主协程阻塞
fmt.Println("yes")
}
``
上述代码将会造成死锁。那么如果我们通过新开一个goroutine来添加数据呢?
```go
package main
import (
"fmt"
)
func main() {
channel := make(chan int, 2)
go func(){
channel <- 1
channel <- 2
channel <- 3 //会导致主协程阻塞
close(channel)
}()
Loop:
for true{
select{
case e,ok := <-channel:
if(ok){
fmt.Println("received:", e)
}else{
fmt.Println("end.")
break Loop
}
}
}
}
上述代码如果不加close(channel),也会造成死锁,因为是由缓冲区的,如果一直没有数据传输进去,那么主协程将会一直等待,造成死锁。
- 3 向已经关闭的channel中写入数据
如果我们从一个关闭的channel中读取数据呢
具体可见下面的代码:
package example
import (
"fmt"
)
func SelectTest() {
channel := make(chan int, 10)
for i := 0; i < 10; i++ {
channel <- i
}
//没有这句话将会导致死锁,为什么会死锁?
/*
首先,对于channel的读和写都是阻塞的,
如果这种阻塞发生在主协程中,并且一顶不会解除阻塞状态,
那么就会发生deadLock现象。
*/
close(channel) //此处的close必须需要,不然将会导致dead lock现象
// 用于控制主协程和子协程之间的同步
syncChannel := make(chan struct{}, 1)
go func() {
Loop:
for true {
select {
case e, ok := <-channel: //如果没有数据,将会持续读取且不停止,那么就会导致阻塞现象,在主协程中,就自然会导致deadLock现象
// 从已经关闭的channel中读取数据的时候
// 如果channel有数据,读取到数据,且ok为true
// 如果channel没有数据,那么读取到的将会是零值,且ok为false
if !ok { //没有元素了
fmt.Println("end")
break Loop
}
fmt.Println("received:", e)
}
}
syncChannel <- struct{}{}
}()
// 如果能够从syncChannel中获取数据,那么就可以结束主协程
// 否则主协程就会阻塞
<-syncChannel
}
总结