select完成对多个channel监控

2025-07-10T11:01:32

假如现在有两个方法,分别有对应的channel,每当他们运行完成之后会向channel中发送一个信号。现在想要知道是哪一个方法先完成,可以如何做?

此时可以使用select方法,来对channel进行监控。

select的语法和switch很像,例如:

func g1(ch chan struct{}) {
    time.Sleep(time.Second)
    ch <- struct{}{}
}

func g2(ch chan struct{}) {
    time.Sleep(2*time.Second)
    ch <- struct{}{}
}

func main() {
    ch1 := make(chan struct{})
    ch2 := make(chan struct{})

    go g1(ch1)
    go g2(ch2)
    select {
    case <-ch1:
        fmt.Println("g1 done")
    case <-ch2:
        fmt.Println("g2 done")
    }
}

运行结果为:

g1 done

假如我现在将这个select放到一个for循环中,不断地监控两个channel,并且想要当超过一定的时间之后就自动退出,也就是设定超时时间,这个时候可以使用 time.NewTimer() 这个方法接收一个时间做为参数,并返回一个timer对象,这个对象有一个成员 C 是一个channel,当时间到达时timer会往这个timer中发送一个信号。我们可以根据这个原理来完成超时时间的设置比如:

func g1(ch chan struct{}) {
    time.Sleep(time.Second)
    ch <- struct{}{}
}

func g2(ch chan struct{}) {
    time.Sleep(2*time.Second)
    ch <- struct{}{}
}

func main() {
    ch1 := make(chan struct{})
    ch2 := make(chan struct{})
    timer := time.NewTimer(4*time.Second)

    go g1(ch1)
    go g2(ch2)
    for {
        select {
        case <-ch1:
            fmt.Println("g1 done")
        case <-ch2:
            fmt.Println("g2 done")
        case <-timer.C:
            fmt.Println("time out!")
            return
        }
    }
}

运行结果如下:

g1 done
g2 done
time out!

同时和switch一样,select也支持default方法,当没有读取到值时就运行default。

关于select还有两点需要注意的:

  1. select时某一个分支就绪了(获取到了值)就执行哪一个分支
  2. 如果多个分支同时就绪,是随机执行,目的是为了防止一直读取某一个分支的值,从而造成”饥饿“现象
当前页面是本站的「Baidu MIP」版。发表评论请点击:完整版 »