channel

创建 channel

1
msg := make(chan int, 10)
  • 基于 channel的通信是同步的
  • 当缓冲区满时,数据发送是阻塞的
  • 通过 make 关键字创建通道时候可以定义缓冲区容量,默认缓冲区容量为0

单向通道

image-20211202231039890

避免生产者 用了消费通道,单向通道可以做限制

关闭通道

image-20211202231149106

轮询多个通道

我需要同时处理多个通道,处理对应的逻辑

image-20211202231258901

定时器 Timer

image-20211202231520288

1
2
3
4
5
6
  timer1 := time.NewTimer(time.Second * 2)
    t1 := time.Now()
    fmt.Printf("t1:%v\n", t1)

    t2 := <-timer1.C
    fmt.Printf("t2:%v\n", t2)

context 原理

1
2
3
4
5
6
type Context interface {
  Deadline() (deadline time.Time, ok bool)
  Done() <-chan struct{}
  Err() error
  Value(key interface{}) interface{}
}

Context的使用最多的地方就是在Golang的web开发中,在http包的Server中,每一个请求在都有一个对应的goroutine去处理。请求处理函数通常会启动额外的goroutine用来访问后端服务,比如数据库和RPC服务。用来处理一个请求的goroutine通常需要访问一些与请求特定的数据,比如终端用户的身份认证信息、验证相关的token、请求的截止时间。 当一个请求被取消或超时时,所有用来处理该请求的 goroutine都应该迅速退出,然后系统才能释放这些goroutine占用的资源。虽然我们不能从外部杀死某个goroutine,所以我就得让它自己结束,之前我们用channel+select的方式,来解决这个问题,但是有些场景实现起来比较麻烦,例如由一个请求衍生出的各个 goroutine之间需要满足一定的约束关系,以实现一些诸如有效期,中止goroutine树,传递请求全局变量之类的功能。

保存上下文

我们可以在上下文中保存任何的类型的数据,用于在整个请求的生命周期去传递使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func middleWare(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    ctx := context.WithValue(req.Context(),"key","value")
    next.ServeHTTP(w, req.WithContext(ctx))
  })
}
 
func handler(w http.ResponseWriter, req *http.Request) {
  value := req.Context().Value("value").(string)
  fmt.Fprintln(w, "value: ", value)
  return
}
 
func main() {
  http.Handle("/", middleWare(http.HandlerFunc(handler)))
  http.ListenAndServe(":8080", nil)
}

超时控制

这里用一个timerCtx来控制一个函数的执行时间,如果超过了这个时间,就会被迫中断,这样就可以控制一些时间比较长的操作,例如io,RPC调用等等。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func longRunningCalculation(timeCost int)chan string{
  result:=make(chan string)
  go func (){
  time.Sleep(time.Second*(time.Duration(timeCost)))
    result<-"Done"
  }()
  return result
}
 
func jobWithTimeoutHandler(w http.ResponseWriter, r * http.Request){
  ctx,cancel := context.WithTimeout(context.Background(), 3*time.Second)
  defer cancel()
 
  select{
  case <-ctx.Done():
    log.Println(ctx.Err())
    return
  case result:=<-longRunningCalculation(5):
    io.WriteString(w,result)
  }
  return
}
 
 
func main() {
  http.Handle("/", jobWithTimeoutHandler)
  http.ListenAndServe(":8080", nil)
}