美文网首页
Golang中Context使用注意事项

Golang中Context使用注意事项

作者: bysir | 来源:发表于2019-01-26 21:29 被阅读0次

    永远记得cancel()

    godoc中有这样一段代码在context.WithTimeout之上:

    func slowOperationWithTimeout(ctx context.Context) (Result, error) {
         ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
         defer cancel()  // releases resources if slowOperation completes before timeout elapses
         return slowOperation(ctx)
     }
    

    当slowOperation在3s内完成了, 如果不defer cancel那么context中使用的资源将都在3s超时之后才会被释放.

    所以养成好的习惯, 使用defer cancel()去及时释放资源.

    Grpc 中 context 会被 cancel.

    在设计合理的系统中, ctx的cancel流程应该是这样的:

    ctx, cancel := context.WithCancel()
    // 当此函数退出时, ctx就应该被销毁, 所以一定得调用cancel让ctx暂用的资源释放以便GC.
    defer cancel()
    doSomething(ctx)
    

    在Grpc中调用服务端接口就是这样设计的: 当客户端请求完服务器, 就会cancel这个ctx.

    下面是个例子

    // 客户端
    c := pb.NewUserClient(conn)
    rsp, err := c.GetUser(context.Background(), &pb.GetUserParams{
        UserId: 1,
    })
    
    // 服务端
    func (*User) GetUser(ctx context.Context, p *GetUserParams) (rsp *pb.User, err error) {
      doSomeThing(ctx)
    }
    

    在以上代码中, 如果在doSomeThing函数中使用到了ctx, 并且使用了异步调用就得注意了

    func doSomathing(ctx context.Context) (err error){
      a:=1;
      go func() {
        doOtherThing(ctx)
      }
    
      return 
    }
    

    现在doOtherThing这个方法不会被正确执行, 因为当客户端调用完GetUser之后就会cancel掉这个ctx, 而不会等待后台协程.

    而如果我们确实需要在后台执行doOtherThing怎么办呢? 那就不要使用这个ctx作为入参了, 而应该使用context.Backgroud().

    cancel代码好像在 google.golang.org/grpc/stream.go:145里, 我也没太花时间追了, 不保证正确, 有兴趣的朋友可以再去看源码研究.

    context.TODO() 和 context.Backgroud() 的区别

    // Background returns a non-nil, empty Context. It is never canceled, has no
    // values, and has no deadline. It is typically used by the main function,
    // initialization, and tests, and as the top-level Context for incoming
    // requests.
    func Background() Context {
        return background
    }
    
    // TODO returns a non-nil, empty Context. Code should use context.TODO when
    // it's unclear which Context to use or it is not yet available (because the
    // surrounding function has not yet been extended to accept a Context
    // parameter).
    func TODO() Context {
        return todo
    }
    

    大致的意思是如果在调用一个函数时, 发现他需要一个ctx, 但是你还不确定传入什么ctx(待开发), 或者就想传递一个nil, 这时候就应该使用 TODO()

    在一个不需要取消的环境中, 如一个长驻运行的http服务, 就应该使用Background()作为给子方法的入参.

    相关文章

      网友评论

          本文标题:Golang中Context使用注意事项

          本文链接:https://www.haomeiwen.com/subject/omiydqtx.html