zoukankan      html  css  js  c++  java
  • Go语言基础之select

    Go语言基础之select

    select多路复用

    在某些场景下我们需要同时从多个通道接收数据。通道在接收数据时,如果没有数据可以接收将会发生阻塞。你也许会写出如下代码使用遍历的方式来实现:

    for{
        // 尝试从ch1接收值
        data, ok :=<-ch1
        // 尝试从ch2接收值
        data, ok :=<-ch2
        …
    }
    

    这种方式虽然可以实现从多个通道接收值的需求,但是运行性能会差很多。为了应对这种场景,Go内置了select关键字,可以同时响应多个通道的操作。

    select的使用类似于switch语句,它有一系列case分支和一个默认的分支。每个case会对应一个通道的通信(接收或发送)过程。select会一直等待,直到某个case的通信操作完成时,就会执行case分支对应的语句。具体格式如下:

    select{
        case <-ch1:
        	// 如果ch1成功读到数据,则进行该case处理语句
            ...
        case data :=<-ch2:
        	// 如果成功向ch2写入数据,则进行该case处理语句
            ...
        case ch3<-data:
            ...
        default:
        	// 如果上面都没有成功,则进入default处理流程
            默认操作
    }
    

    举个小例子来演示下select的使用:

    func main() {
        ch := make(chan int, 1)
    	for i := 0; i < 10; i++ {
    		select {
    		case x := <-ch:
    			fmt.Println("x", x)
    		case ch <- i:
    			fmt.Println("i=", i)
    		default:
    			fmt.Println("default=", i)
    
    		}
    	}
    }
    

    i= 0
    x 0
    i= 2
    x 2
    i= 4
    x 4
    i= 6
    x 6
    i= 8
    x 8

    • select可以同时监听一个或多个channel,直到其中一个channel ready
    package main
    
    import (
       "fmt"
       "time"
    )
    
    func test1(ch chan string) {
       time.Sleep(time.Second * 5)
       ch <- "test1"
    }
    func test2(ch chan string) {
       time.Sleep(time.Second * 2)
       ch <- "test2"
    }
    
    func main() {
       // 2个管道
       output1 := make(chan string)
       output2 := make(chan string)
       // 跑2个子协程,写数据
       go test1(output1)
       go test2(output2)
       // 用select监控
       select {
       case s1 := <-output1:
          fmt.Println("s1=", s1)
       case s2 := <-output2:
          fmt.Println("s2=", s2)
       }
    }
    

    s2= test2

    • 如果多个channel同时ready,则随机选择一个执行
    package main
    
    import (
       "fmt"
    )
    
    func main() {
       // 创建2个管道
       int_chan := make(chan int, 1)
       string_chan := make(chan string, 1)
       go func() {
          //time.Sleep(2 * time.Second)
          int_chan <- 1
       }()
       go func() {
          string_chan <- "hello"
       }()
       select {
       case value := <-int_chan:
          fmt.Println("int:", value)
       case value := <-string_chan:
          fmt.Println("string:", value)
       }
       fmt.Println("main结束")
    }
    

    int: 1
    main结束

    • 可以用于判断管道是否存满
    package main
    
    import (
       "fmt"
       "time"
    )
    
    // 判断管道有没有存满
    func main() {
       // 创建管道
       output1 := make(chan string, 10)
       // 子协程写数据
       go write(output1)
       // 取数据
       for s := range output1 {
          fmt.Println("res:", s)
          time.Sleep(time.Second)
       }
    }
    
    func write(ch chan string) {
       for {
          select {
          // 写数据
          case ch <- "hello":
             fmt.Println("write hello")
          default:
             fmt.Println("channel full")
          }
          time.Sleep(time.Millisecond * 500)
       }
    }
    

    image-20211107104838689

    总结

    使用select语句能提高代码的可读性。

    • 可处理一个或多个channel的发送/接收操作。
    • 如果多个case同时满足,select会随机选择一个。
    • 对于没有caseselect{}会一直等待,可用于阻塞main函数。
  • 相关阅读:
    C#输出JS代码封装类Alart
    我的汇编学习之路(2)win8(64位)下安装debug
    .NET使用一般处理程序生成验证码
    ?运算符(null合并运算符)和三木运算符
    讲解:小菜鸟自制的.NET实体类生成器
    我的汇编学习之路(1)进制转换
    未来
    callee,caller区别
    string::size_type
    ubuntu 12.04 LTS u盘安装
  • 原文地址:https://www.cnblogs.com/randysun/p/15520053.html
Copyright © 2011-2022 走看看