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
条件,说明信道已经满了。
注意点
- 没有default时,select语句会一直等待,直到某个case里的IO操作可以进行
- case条件中包含的【通道表达式】和【元素表达式】都会先被求值。无论它们所在的case是否有可能被选择都会这样。
- 如果有多个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 控制
通过在一个goroutine中发送控制信号控制其他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)
因此,在程序第一次执行到此处时,要注意对chan的控制
有疑问加站长微信联系(非本文作者)