go - select 从已经关闭的channel中读取数据注意的问题 -可能会发生的dead lock现象

相信大家在从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
}

总结
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小吴同学GOGOGO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值