• Go channel阻塞,死锁, fatal error: all goroutines are asleep deadlock!


    在使用channel消息通道的时候不注意的话,就会出现死锁的情况,这种情况的原因一般都是并发执行的时候,一个协程对A通道做写入或读取操作,而另一个协程对A做对应的操作,同时A的缓存用完了或者没有设置缓存,这时候就会导致两个协程互相等待对方操作,成为死锁.

        c1 := make(chan int)
        c2 := make(chan int)
        c3 := make(chan [2]int)
        go func() {
            for {
                fmt.Println("Loop . . .")
                select {
                case i := <-c1 :
                    fmt.Println("Got num from C1", i)
                    c3 <- [2]int{1, i}
                case j := <-c2:
                    fmt.Println("Got num from C2", j)
                    c3 <- [2]int{2, j}
                default:
                    c3 <- [2]int{2, 0}
                }
                fmt.Println("Sleep . . .")
                time.Sleep(time.Second)
            }
        }()
        for  {
            fmt.Println("Insert into Channel01")
            c1 <- 13
            fmt.Println("Insert into Channel02")
            c2 <- 14
            fmt.Println("Insert into Channel03")
            for sign := range c3 {
               fmt.Println("got from c3:", sign)
               c1 <- 23
               c2 <- 24
            }
        }
    

    如上就会出现死锁
    channel定义的时候可以设置为有缓存和无缓存,通过make的第二个参数指定,上面的代码都没有指定该参数,所以创建的channel为无缓存的.
    无缓存channel的特性为,一个通道不会保存任何数据,任一协程往里面放数据,都必须等待另一个使用该通道数据的协程从里面拿走,或者协程从里面拿数据的时候,必须有另一个协程往里面放东西,由于没有容量,所以放数据的协程必须等待放入的数据被消费才会执行后面的代码.举个例子就是你和另一个人协同工作,现在你要把一个东西交给另一个人,你把这个东西放在手上,如果另一个人不从你手里拿走的话,你就干不了剩下的事.

    如图,两个协程分别是1和2
    协程1走到1-1的时候,会等待C1出现数据,在协程2的2-1会放入数据,这时候协程1可以往下走,执行打印,然后等待往c3放数据,但是协程2走完2-1之后,在2-2会往c2中放数据,由于三个通道都没缓存,数据必须是及时消费的,c2在2-2的时候放入数据就会等待消费,不会往下执行,直到c2的数据被消费为止.但是协程1的1-2步骤也和2-2一样,等待被消费,这时候两个协程同时阻塞了.
    能够打破同时阻塞的代码在1-3,也就是c2的数据被消费,但是这一步要等待1-2步骤完成,由于1-2操作的c3一直没有消费者,所以1-3就永远走不到了.2-2走不下去,同样下面对c3的消费也不会走到.

    这就导致了死锁的情况.

    执行结果如上图

    解决办法有两个,一个是及时消费,但是这涉及到代码逻辑,不可能强行消费,另一个办法就是创建channel的时候设定缓存值.
    相当于在两个协程之间放了一个桌子,这样A可以把数据放在桌子上,然后继续做自己的事,B需要数据可以从桌子上拿,当然如果拿数据的协程拿不到数据,同样还会阻塞,这里可以用select来处理,执行default来避免阻塞.
    如图:

    改成这样,即使容量只设置成1,程序也会执行下去,不会再被阻塞:

  • 相关阅读:
    PHP版根据经纬度和半径计算出经纬度的范围
    使用GPS经纬度定位附近地点(某一点范围内查询)
    sql语句查询经纬度范围
    Android检测是否安装了指定应用
    Android 定时器实现的几种方式和removeCallbacks失效问题详解
    Android Service与Activity之间通信的几种方式
    Android操作系统11种传感器介绍
    Android录音--AudioRecord、MediaRecorder
    Android广播接收者应用(电话拦截器)
    收藏夹
  • 原文地址:https://www.cnblogs.com/haiton/p/15623321.html
Copyright © 2020-2023  润新知