Skip to content

channel

channel - 多线程通信

code

  • 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)

遍历通道缓冲区

code

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)

单向通道

code

  • 只发送通道
    • 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 } 
}

关闭通道

code

  • 通道无需每次关闭
  • 关闭的作用是告诉接收者该通道再无新数据发送
  • 只有发送方需要关闭通道
ch := make(chan int)
defer close(ch)
if v, notClosed := <-ch; notClosed {
    fmt.Println(v)
}

select

code

  • 当多个协程同时运行时,可通过 select 轮询多个通道
  • 如果所有通道都阻塞则等待,如定义了 default 则执行 default
  • 如多个通道就绪则随机选择
select {
    case v:= <- ch1:
        ...
    case v:= <- ch2:
        ...
    default: 
        ...
}

定时器 Timer

code

  • 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

code

  • 超时、取消操作或者一些异常情况,往往需要进行抢占操作或者中断后续操作
  • 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())