美文网首页
golang select多路复用

golang select多路复用

作者: 护念 | 来源:发表于2023-11-13 20:50 被阅读0次

select

在开始介绍select之前,我们先说一个概念多路复用

什么是多路复用呢?
我们从一个大家所熟知的场景说起,比如在一个学校里面要举行运动会,运动会上有许多的比赛项目,比如:100米短跑、跳远、跳绳、篮球等等;总之很多,这些项目许多都是同时进行的,做为学校广播室的播音员,需要实时报道各项目的进行情况;在这种情况下就需要,播音员随时根据各个项目的比赛进度,进行报道;

同时关注各个项目的比赛进度,一旦有进展,就进行报道;这个过程我们就可以称之为多路复用。

类比到计算机里,我们有多个gorountine同时在进行,我们需要实时监控各个gorountine的执行情况;在go中是通过select实现的。

select通过对多个通道的监控,来实现不同功能,比如监控到通道1有值时做A任务;通道2有值做B任务。

1. select特点

  1. 哪个通道准备好,执行哪个通道
  2. 多个通道都准备好,从中随意选择执行
  3. 不带default,没有通道准备好,则阻塞
  4. 带default,没有通道准备好,则执行default

2. select用法

它的语法如下:

select {
  case // 通道1xx:
    // 执行A操作
  case // 通道2xx:
    // 执行B操作
  default:
    // 执行默认操作
}

比如:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 初始化两个通道
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(time.Second)
        ch1 <- "channle 1 string"
    }()

    go func() {
        time.Sleep(time.Second)
        ch2 <- "channle 2 string"
    }()

    // 这里等待两个通道 所以for写了两次
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1: // 通道1接收到值
            fmt.Println("recevie: ", msg1)
        case msg2 := <-ch2: // 通道2接收到值
            fmt.Println("recevie: ", msg2)
        }
    }
}

3. select 带default

先看代码,我们直接在上面的代码中加上default,执行看看:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 初始化两个通道
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(time.Second)
        ch1 <- "channle 1 string"
    }()

    go func() {
        time.Sleep(time.Second)
        ch2 <- "channle 2 string"
    }()

    // 这里等待两个通道 所以for写了两次
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1: // 通道1接收到值
            fmt.Println("recevie: ", msg1)
        case msg2 := <-ch2: // 通道2接收到值
            fmt.Println("recevie: ", msg2)
        default:
            fmt.Println("execute select default")
        }
    }
}

会发现执行结果为:

execute select default
execute select default

为啥两次都执行到默认值呢,可能你会比较困惑。因为在for循环两次执行select的时,ch1ch2都没有准备好(他们阻塞在sleep),所以都去执行default了;default执行的非常快,两次执行完花费的时间通道都没有准备好,有兴趣你可以适当延长default执行时间试试。

上面的default虽然加了,但是没啥意义,下面我们写一个稍微有意义的代码:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 初始化两个通道
    ch := make(chan string)

    go func() {
        // 向通道发送10次值
        for i := 0; i < 10; i++ {
            time.Sleep(time.Second)
            ch <- fmt.Sprintf("%v time msg", i)
        }
    }()

    // 定时器 5s后触发
    timer := time.NewTimer(5 * time.Second)
    for {
        select {
        case msg := <-ch: // 通道接收到值
            fmt.Println("recevie: ", msg)
        case <-timer.C: // 5s后结束
            fmt.Println("finished!")
            return // 退出for
        default:
            time.Sleep(1 * time.Second) // 避免default执行太快 增加耗时
            fmt.Println(time.Now(), "execute select default")
        }
    }
}

执行输入如下

2023-11-12 21:59:06.38006 +0800 CST m=+1.001353869 execute select default
recevie:  0 time msg
2023-11-12 21:59:07.380926 +0800 CST m=+2.002264357 execute select default
recevie:  1 time msg
2023-11-12 21:59:08.38216 +0800 CST m=+3.003541897 execute select default
recevie:  2 time msg
2023-11-12 21:59:09.382799 +0800 CST m=+4.004225373 execute select default
recevie:  3 time msg
2023-11-12 21:59:10.38338 +0800 CST m=+5.004850410 execute select default
recevie:  4 time msg
finished!

相关文章

网友评论

      本文标题:golang select多路复用

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