channel
channel - 多线程通信
- Channel 是多个协程之间通讯的管道
- 一端发送数据,一端接收数据
- 同一时间只有一个协程可以访问数据,无共享内存模式可能出现的内存竞争
- 协调协程的执行顺序
- 声明方式
- var identifier chan datatype
- 操作符<-
- 示例
ch := make(chan int)
go func() {
fmt.Println("hello from goroutine")
ch <- 0 //数据写入Channel
}()
i := <-ch//从Channel中取数据并赋值
通道缓冲
- 基于 Channel 的通信是同步的
- 当缓冲区满时,数据的发送是阻塞的
- 通过 make 关键字创建通道时可定义缓冲区容量,默认缓冲区容量为 0
- 下面两个定义的区别?
- ch := make(chan int)
- ch := make(chan int,1)
遍历通道缓冲区
ch := make(chan int, 10)
go func() {
for i := 0; i < 10; i++ {
rand.Seed(time.Now().UnixNano())
n := rand.Intn(10) // n will be between 0 and 10
fmt.Println("putting: ", n)
ch <- n
}
close(ch)
}()
fmt.Println("hello from main")
for v := range ch {
fmt.Println("receiving: ", v)
单向通道
- 只发送通道
- var sendOnly chan<- int
- 只接收通道
- var readOnly <-chan int
- Istio webhook controller
- func (w *WebhookCertPatcher) runWebhookController(stopChan <-chan struct{}) {}
- 如何用: 双向通道转换
var c = make(chan int)
go prod(c)
go consume(c)
func prod(ch chan<- int){
for { ch <- 1 }
}
func consume(ch <-chan int) {
for { <-ch }
}
关闭通道
- 通道无需每次关闭
- 关闭的作用是告诉接收者该通道再无新数据发送
- 只有发送方需要关闭通道
ch := make(chan int)
defer close(ch)
if v, notClosed := <-ch; notClosed {
fmt.Println(v)
}
select
- 当多个协程同时运行时,可通过 select 轮询多个通道
- 如果所有通道都阻塞则等待,如定义了 default 则执行 default
- 如多个通道就绪则随机选择
select {
case v:= <- ch1:
...
case v:= <- ch2:
...
default:
...
}
定时器 Timer
- time.Ticker 以指定的时间间隔重复的向通道 C 发送时间值
- 使用场景
- 为协程设定超时时间
timer := time.NewTimer(time.Second)
select {
// check normal channel
case <-ch:
fmt.Println("received from ch")
case <-timer.C:
fmt.Println("timeout waiting from channel ch")
}
上下文 Context
- 超时、取消操作或者一些异常情况,往往需要进行抢占操作或者中断后续操作
- Context 是设置截止日期、同步信号,传递请求相关值的结构体
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
用法: - context.Background - Background 通常被用于主函数、初始化以及测试中,作为一个顶层的 context,也就是说一般我们创建的 context 都是基于 Background - context.TODO - TODO 是在不确定使用什么 context 的时候才会使用 - context.WithDeadline - 超时时间 - context.WithValue - 向 context 添加键值对 - context.WithCancel - 创建一个可取消的 context
如何停止一个子协程
done := make(chan bool)
go func() {
for {
select {
case <-done:
fmt.Println("done channel is triggerred, exit child go routine")
return
}
}
}()
close(done)
基于 Context 停止子协程
- Context 是 Go 语言对 go routine 和 timer 的封装
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
go process(ctx, 100*time.Millisecond) <-ctx.Done()
fmt.Println("main:", ctx.Err())