zoukankan      html  css  js  c++  java
  • golang对通道进行select,case生效异常问题

    项目开发中,使用golang的channel进行线程内的消息传递,由于使用了多个channel,所以使用select case对通道进行消息监听,处理最先发生变化的channel,但是出现了一直监听不到的情况,程序总是执行到select 中的default处理块。

    下面是示例代码:

    import "fmt"
    
    func main() {
        ch1:= make(chan string)
    
        go func() { // 开启一个协程运行该函数
            select {
            case msg := <-ch1: // 从通道ch1中接收数据,并将数据赋值给msg
                fmt.Println("received msg", msg)
            default:
                fmt.Println("no msg received")
            }
        }()
    
        // 主线程
        msg := "hi"
        select {
        case ch1 <- msg: // 发送值msg到通道ch1中
            fmt.Println("sent msg", msg)
        default:
            fmt.Println("no msg sent")
        }
    
    }
    

      

    预期的输出结果是:

    received msg

    sent msg

     

    而实际结果是:

    no msg received

    no msg sent

     

     

     

    原因分析


    我们设置的是无缓存的通道ch1(没有在声明时设置通道容量),这种通道有个特点,就是只有sender和receiver都准备好了后它们的通讯(communication)才会发生(Blocking),无缓存的channel只有在receiver准备好后send才被执行。

    从上述特点来看,怀疑是协程中的receiver还没准备好,导致主线程中的sender无法被case,因而输出了no msg sent。

    PS:当 select 中的其他条件分支都没有准备好的时候,default 分支会被执行。

     

    换句话说,就是协程和主线程同时运行,但是协程代码的执行速度低于主线程的代码导致。

    测试方式:往主线程加个延时器

    import "fmt"
    import "time"
    
    func main() {
        ch1:= make(chan string)
    
        go func() { // 开启一个协程运行该函数
            select {
            case msg := <-ch1: // 从通道ch1中接收数据,并将数据赋值给msg
                fmt.Println("received msg", msg)
            default:
                fmt.Println("no msg received")
            }
        }()
    
        // 加入延时器 
        time.Sleep(100 * time.Millisecond)
    
        // 主线程
        msg := "hi"
        select {
        case ch1 <- msg: // 发送值msg到通道ch1中
            fmt.Println("sent msg", msg)
        default:
            fmt.Println("no msg sent")
        }
    
    }
    

      

     

    打印结果符合预期:

    received msg

    sent msg

     

    上述结果确认了猜测,协程的执行速度低于主线程,因此针对协程开启的效率进行相关资料的查阅,确认了:

    go新建一个goroutine需要一点时间,主线程走到select块的时候,接收消息的的goroutine一般都还没运行起来。

     

     

     

     

    解决方案


    根据实际情况,设置有缓存通道类型接口。

    eg:

    make(chan string, 100)
    

      

     

     

  • 相关阅读:
    2016-7-4工作总结
    2016-7第一周工作总结
    2016-6-30 工作总结
    2016-6-29 工作总结
    2016-6-28 工作总结
    基于软件开发对嵌入式开发的思考
    团队项目总结
    软件工程课程总结
    图描述之:流程图
    004-二叉树的遍历
  • 原文地址:https://www.cnblogs.com/yourstars/p/15186585.html
Copyright © 2011-2022 走看看