Golang 并发控制神器-- context

329 阅读2分钟

context 的简介

许多编程语言中都有上下文的概念,Go语言中也提供了这种特殊的数据结构 --context.Context,用来在多个goroutine之间设置超时时间、同步信号以及传递值。Context的操作是并发安全的,使用时不用担心数据并发安全的问题。

context 的使用

在web开发中,我们一般对每个请求都会开启一个goroutine 来处理;但是我们在处理请求的时候,不仅会去查询数据库,还会调用Http 或者RPC 服务,考虑到性能等因素,我们一般会并发的处理这些调用。所以在这个处理请求的 goroutine 中我们还会创建 n 个 goroutine。 这时我们就可以借助 context 来控制多个 goroutine。

  • 在多个goroutine 之间共享值 一般我们对每次请求都会创建一个logId 来标记这次请求,在打印日志的时候会带上这个logId
const key ="logId"

func fn1(ctx context.Context){
     if v:=ctx.Value(key);v!=nil{
       logger.Info("logId",v)
     }
}

func fn2(ctx context.Context){
  if v:=ctx.Value(key);v!=nil{
       logger.Info("logId",v)
   }
}

func main(){
 ctx := context.WithValue(context.Background(), key, "1234")
 go fn1(ctx)
 go fn2(ctx)
}

  • timeout 控制 当我们请求下游服务时,通常对请求会有个超时的限制,当在限制的时间内没有返回,会抛出错误

func request(ctx context.Context,url string){
   req,err:=http.NewRequestWithContext(ctx,'GET',url,nil)
   if err!=nil {
     logger.Error(err)
   }
   resp,err= http.DefaultClient.Do(req)
   if err!=nil {
     logger.Error(err)
   }
  // ...
}

 func main(){
    ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
    defer cancel()
    go request(ctx,"https://www.baidu.com")
 }

  • cancel 控制

比如在从数据库查询商品信息的时候抛出异常了,这时我们要返回给用户服务端异常,但是调用商品评论详情的API 还未结束,为了不浪费资源,我们需要取消这个操作。

// 商品详情
func goodsInfo(ctx context.Context) error{
 result := make(chan interface{})
 //...查询数据库
 
 select {
    case <-ctx.Done():
       return ctx.Err()
    case <-result:
      return nil
 }
}

// 商品评论
func goodsComment(ctx context.Context) error{
 result := make(chan interface{})
 //... 查询数据库
 
 select {
    case <-ctx.Done():
       return ctx.Err()
    case <-result:
      return nil
 }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go func(){
       if err:=goodsInfo(ctx);err!=nil{
        cancel()
       }
    }() 
    
    go func(){
       if err:=goodsComment(ctx);err!=nil{
        cancel()
       }
    }() 
}

小结

我们通过上面的例子,了解了在开发中使用context的几种场景。但是作为一个对技术有追求的人,难道不好奇context 怎么实现这些控制的吗,下一章节我们就对 context 的源码进行分析下。