乐趣区

关于go:Go-快速入门指南-select

概述

select 相似 switch, 蕴含一系列逻辑分支和一个可选的默认分支。每一个分支对应通道上的一次操作 (发送或接管),
能够将 select 了解为专门针对通道操作的 switch 语句

语法规定

select {
case v1 := <- ch1:
// do something ...  
case v2 := <- ch2:
// do something ...
default:
// do something ...
}

执行程序

  • 当同时存在多个满足条件的通道时,随机抉择一个执行
  • 如果没有满足条件的通道时,检测是否存在 default 分支

    • 如果存在则执行
    • 否则阻塞期待

通常状况下,把含有 default 分支 select 操作称为 无阻塞通道操作

例子

随机执行一个

package main

import (
    "fmt"
    "time"
)

func main() {ch1 := make(chan string)
    ch2 := make(chan string)
    done := make(chan bool)

    go func() {ch1 <- "hello"}()

    go func() {ch2 <- "world"}()

    go func() {done <- true}()

    time.Sleep(time.Second) //  休眠 1 秒

    // 此时 3 个通道应该都满足条件,select 会随机抉择一个执行
    select {
    case msg := <-ch1:
        fmt.Printf("ch1 msg = %s\n", msg)
    case msg := <-ch2:
        fmt.Printf("ch2 msg = %s\n", msg)
    case <-done:
        fmt.Println("done !")
    }

    close(ch1)
    close(ch2)
    close(done)
}

// $ go run main.go
// 输入如下,你的输入可能和这里的不一样, 多运行几次看看成果
/**
  ch1 msg = hello
*/

default (无阻塞通道操作)

package main

import (
    "fmt"
    "time"
)

func main() {ch1 := make(chan string)
    ch2 := make(chan string)
    done := make(chan bool)

    go func() {time.Sleep(time.Second)
        ch1 <- "hello"
    }()

    go func() {time.Sleep(time.Second)
        ch2 <- "world"
    }()

    go func() {time.Sleep(time.Second)
        done <- true
    }()

    // 此时 3 个通道都在休眠中, 不满足条件,select 会执行 default 分支
    select {
    case msg := <-ch1:
        fmt.Printf("ch1 msg = %s\n", msg)
    case msg := <-ch2:
        fmt.Printf("ch2 msg = %s\n", msg)
    case <-done:
        fmt.Println("done !")
    default:
        fmt.Println("default !")
    }

    close(ch1)
    close(ch2)
    close(done)
}

// $ go run main.go
// 输入如下
/**
  default !
*/

和 for 搭配应用

通过在 select 外层加一个 for 循环,能够达到 有限轮询 的成果。

package main

import (
    "fmt"
    "time"
)

func main() {ch1 := make(chan string)
    ch2 := make(chan string)
    done := make(chan bool)

    go func() {
        // ch1 goroutine 输入 1 次 
        fmt.Println("[ch1 goroutine]")
        time.Sleep(time.Second)
        ch1 <- "hello"
    }()

    go func() {
        // ch2 goroutine 输入 2 次
        for i := 0; i < 2; i++ {fmt.Println("[ch2 goroutine]")
            time.Sleep(time.Second)
        }
        ch2 <- "world"
    }()

    go func() {
        // done goroutine 输入 3 次
        for i := 0; i < 3; i++ {fmt.Println("[done goroutine]")
            time.Sleep(time.Second)
        }
        done <- true
    }()

    for exit := true; exit; {
        select {
        case msg := <-ch1:
            fmt.Printf("ch1 msg = %s\n", msg)
        case msg := <-ch2:
            fmt.Printf("ch2 msg = %s\n", msg)
        case <-done:
            fmt.Println("done !")
            exit = false // 通过变量管制外层 for 循环退出
        }
    }

    close(ch1)
    close(ch2)
    close(done)
}

// $ go run main.go
// 输入如下,你的输入程序可能和这里的不一样
/**
  [done goroutine]
  [ch2 goroutine]
  [ch1 goroutine]
  ch1 msg = hello
  [done goroutine]
  [ch2 goroutine]
  ch2 msg = world
  [done goroutine]
  done !
*/

从输入后果看,[ch1 goroutine] 输入了 1 次,[ch2 goroutine] 输入了 2 次,[done goroutine] 输入了 3 次。

分割我

退出移动版