/ 城寒 / Golang - Select & Switch

Golang - Select & Switch

2018-01-27 posted in [Golang]

select

golang的select功能和C中的select, poll, epoll类似,就是监听 IO 操作,当 IO 操作发生时,触发相应的动作。

ch1 := make (chan int, 1)
ch2 := make (chan int, 1)

select {
case <-ch1:
    fmt.Println("ch1 pop one element")
case <-ch2:
    fmt.Println("ch2 pop one element")
}

select 的代码形式和 switch 非常相似, 不过 select 的 case 里的操作语句只能是【IO 操作】 。 此示例里面 select 会一直等待等到某个 case 语句完成, 也就是等到成功从 ch1 或者 ch2 中读到数据。 则 select 语句结束。

使用 select 实现 timeout 机制

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!")
}

同时等待读取ch和timeout,当超时时间到的时候,<- timeout会操作成功。 所以 select 语句则会退出。 而不是一直阻塞在 ch 的读取操作上。 从而实现了对 ch 读取操作的超时设置。 如果select包含default,则上述两个都无法成功读取的情况下,直接进入default。因此,可以在select中放入case ch <- 1,如果进入default条件,说明信道已经满了。

注意点

  1. 没有default时,select语句会一直等待,直到某个case里的IO操作可以进行
  2. case条件中包含的【通道表达式】和【元素表达式】都会先被求值。无论它们所在的case是否有可能被选择都会这样。
  3. 如果有多个case同时可以运行,go会随机选择一个case执行

switch

golang中switch有两种判断条件:表达式、类型(boolean-expression or integral type)

switch marks {
    case 90: grade = "A"
    case 80: grade = "B"
    case 50,60,70 : grade = "C"
    default: grade = "D" 
}
switch {
    case grade == "A" :
        fmt.Printf("Excellent!\n" )     
    case grade == "B", grade == "C" :
        fmt.Printf("Well done\n" )      
    case grade == "D" :
        fmt.Printf("You passed\n" )      
    case grade == "F":
        fmt.Printf("Better try again\n" )
    default:
        fmt.Printf("Invalid grade\n" );
}
switch i := x.(type) {
    case nil:   
        fmt.Printf("type of x :%T",i)                
    case int:   
        fmt.Printf("x is int")                               
    case func(int) float64:
        fmt.Printf("x is func(int)")                      
    case bool, string:
        fmt.Printf("x is bool or string")       
    default:
        fmt.Printf("don't know the type")     
}  

使用select + switch实现 goroutine 控制

package main
import (
    "fmt"
    "runtime"
    "sync"
)
// Possible worker states.
const (
    Stopped = 0
    Paused  = 1
    Running = 2
)
// Maximum number of workers.
const WorkerCount = 1000
func main() {
    // Launch workers.
    var wg sync.WaitGroup
    wg.Add(WorkerCount + 1)
    workers := make([]chan int, WorkerCount)
    for i := range workers {
        workers[i] = make(chan int, 1)
        go func(i int) {
            worker(i, workers[i])
            wg.Done()
        }(i)
    }
    // Launch controller routine.
    go func() {
        controller(workers)
        wg.Done()
    }()
    // Wait for all goroutines to finish.
    wg.Wait()
}
func worker(id int, ws <-chan int) {
    state := Paused // Begin in the paused state.
    for {
        select {
        case state = <-ws:
            switch state {
            case Stopped:
                fmt.Printf("Worker %d: Stopped\n", id)
                return
            case Running:
                fmt.Printf("Worker %d: Running\n", id)
            case Paused:
                fmt.Printf("Worker %d: Paused\n", id)
            }
        default:
            // We use runtime.Gosched() to prevent a deadlock in this case.
            // It will not be needed of work is performed here which yields
            // to the scheduler.
            runtime.Gosched()
            if state == Paused {
                break
            }
            // Do actual work here.
        }
    }
}
// controller handles the current state of all workers. They can be
// instructed to be either running, paused or stopped entirely.
func controller(workers []chan int) {
    // Start workers
    setState(workers, Running)
    // Pause workers.
    setState(workers, Paused)
    // Unpause workers.
    setState(workers, Running)
    // Shutdown workers.
    setState(workers, Stopped)
}
// setState changes the state of all given workers.
func setState(workers []chan int, state int) {
    for _, w := range workers {
        w <- state
    }
}

注意,此处有坑!!! 使用该方法有可能导致程序在启动时死锁,并不能在controller中随意的暂停和启动 比如,一开始写入 暂停 ,此时worker暂停,导致不能从worker chan中消费数据,导致controller不能向worker chan写入run状态(chan长度为1)