美文网首页
Go select 学习笔记

Go select 学习笔记

作者: Jaycee88 | 来源:发表于2020-05-29 13:23 被阅读0次

select 语句的行为

select就是用来监听和channel有关的IO操作

// https://talks.golang.org/2012/concurrency.slide#32
select {
case v1 := <-c1:
    fmt.Printf("received %v from c1\n", v1)
case v2 := <-c2:
    fmt.Printf("received %v from c2\n", v1)
case c3 <- 23:
    fmt.Printf("sent %v to c3\n", 23)
default:
    fmt.Printf("no one was ready to communicate\n")
}

上面这段代码中,select 语句有四个 case 子语句,前两个是 receive 操作,第三个是 send 操作,最后一个是默认操作。代码执行到 select 时,case 语句会按照源代码的顺序被评估,且只评估一次,评估的结果会出现下面这几种情况:

  1. 除 default 外,如果只有一个 case 语句评估通过,那么就执行这个case里的语句;
  2. 除 default 外,如果有多个 case 语句评估通过,那么通过伪随机的方式随机选一个;
  3. 如果 default 外的 case 语句都没有通过评估,那么执行 default 里的语句;
  4. 如果没有 default,那么 代码块会被阻塞,直到有一个 case 通过评估;否则一直阻塞
  5. 当case上读取一个通道时,如果这个通道是 nil , 则该case 永远阻塞
  6. select{} 永远阻塞

select 的使用场景

  1. 无阻塞的读、写通道。即使通道是带缓存的,也是存在阻塞的情况,使用select可以完美的解决阻塞读写
  2. 给某个请求/处理/操作,设置超时时间,一旦超时时间内无法完成,则停止处理
  3. select本色:多通道处理
  4. done channel
  5. quit channel

解决阻塞的2种办法

  1. 使用select的default语句,在channel不可读写时,即可返回
  2. 使用select+定时器,在超时时间内,channel不可读写,则返回(推荐方式)
// 超时demo
timeout := make (chan bool, 1)
go func() {
    time.Sleep(1e9) // sleep one second
    timeout <- true
}()
ch := make (chan int)
select {
case <- ch:
case <- timeout:
    fmt.Println("timeout!")
}
// 定时器demo
func main() {
   tickTimer := time.NewTicker(1 * time.Second)
   barTimer := time.NewTicker(60 * time.Second)
   for {
    select {
    case <-tickTimer.C:
        fmt.Println("tick")
    case <-barTimer.C:
        fmt.Println("bar")
    }
   }
}

当case上读一个通道时,如果这个通道是nil,则该case永远阻塞。这个功能有1个妙用,select通常处理的是多个通道,当某个读通道关闭了,但不想select再继续关注此case,继续处理其他case,把该通道设置为nil即可。
下面是一个合并程序等待两个输入通道都关闭后才退出的例子,就使用了这个特性。

func combine(inCh1, inCh2 <-chan int) <-chan int {
    // 输出通道
    out := make(chan int)

    // 启动协程合并数据
    go func() {
        defer close(out)
        for {
            select {
            case x, open := <-inCh1:
                if !open {
                    inCh1 = nil
                    continue
                }
                out<-x
            case x, open := <-inCh2:
                if !open {
                    inCh2 = nil
                    continue
                }
                out<-x
            }

            // 当ch1和ch2都关闭是才退出
            if inCh1 == nil && inCh2 == nil {
                break
            }
        }
    }()

    return out
}

https://mp.weixin.qq.com/s/rXJFAWY-WRp6z5mkBGJtGg
https://segmentfault.com/a/1190000017410112
https://xuchao918.github.io/2019/05/17/Go%E5%B9%B6%E5%8F%91-%E4%BD%BF%E7%94%A8%E5%8D%8F%E7%A8%8B%E3%80%81%E9%80%9A%E9%81%93%E5%92%8Cselect/

相关文章

网友评论

      本文标题:Go select 学习笔记

      本文链接:https://www.haomeiwen.com/subject/uneruhtx.html