欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > Go - context包的使用

Go - context包的使用

2025/2/27 12:28:02 来源:https://blog.csdn.net/codekingo/article/details/143760091  浏览:    关键词:Go - context包的使用

context简介

  • context用于在多个goroutine中传递上下文信息,且相同的context传递给运行在不同goroutine中的函数是并发安全的。
  • context包定义的上下文类型,可以使用backgroundTODO创建一个上下文。也可以使用WithDeadlineWithTimeoutWithCancelWithValue创建的修改副本替换它。

因为context具有在多个不同goroutine中传递上下文信息的性质,所以其常用于并发控制。

下面是对context包的详细使用:

context的使用

创建一个context

context包提供了两种创建方式:

  • context.Background()
  • context.TODO()
  • 前者是上下文的默认值,所有其他的上下文一般都从它衍生而来。
  • 后者是在不确定使用哪种上下文时使用。
    所以,大多数情况下,都使用context.Background()来作为上下文进行传递。

这两种方式创建出来的是根context,不具有任何功能。需要根据实际选择context包提供的With系列函数来解决相应的问题。

下面是context包提供的With系列函数:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context

context的衍生具有树形结构的特点。

  • 这四个函数所返回的context都是基于父级context所衍生的。而其返回的context依然可以作为父节点,衍生出其他子节点。
  • 如果一个context节点被取消,那么从其所衍生出来的context子节点都会被取消。

下面是对这些With函数的具体使用介绍:

With系列函数

WithCancel 取消控制

WithCancel的作用是,我们可以通过传递这样的上下文,去控制多个goroutine,通过cancel函数,在任意时刻让这些goroutine取消。

下面是例子:

func main() {  ctx, cancel := context.WithCancel(context.Background())  go task(ctx)  time.Sleep(5 * time.Second)  cancel()  time.Sleep(time.Second)  
}  func task(ctx context.Context) {  for range time.Tick(time.Second) {  select {  case <-ctx.Done():  fmt.Println(ctx.Err())  return  default:  fmt.Println("tasking...")  }    }
}

超时控制

一个健壮的程序都是需要设置超时时间的,避免由于服务端长时间响应而消耗资源。所以,一些web框架都会采用WithDeadlineWithTimeOut函数来做超时控制。

  • WithDeadlineWithTimeout的作用是一样的,只是传递的时间参数不同而已,它们都会在超过传递的时候后,自动取消context
  • 需要注意的是,两者也会返回CancelFunc的函数,即便是被自动取消,也需要在结束后,手动取消一下,避免消耗不必要的资源。

下面是例子:

func main() {  ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)  defer cancel()  go task(ctx)  time.Sleep(6 * time.Second)  
}  func task(ctx context.Context) {  for range time.Tick(time.Second) {  select {  case <-ctx.Done():  fmt.Println(ctx.Err())  return  default:  fmt.Println("tasking...")  }    }
}

WithValue 携带数据

WithValue函数可以返回一个可携带数据的context,可以用于在多个goroutine进行传递。
例如,在日常业务开发中,需要有一个trace_id来串联所有日志,那么就可以使用WithValue来实现。

需要注意的是,通过WithValue得到的context所携带的数据,是可以传递给从其衍生出来的子节点。简单来说,该context的整棵子树都会携带这个数据。

下面是例子:

func main() {  ctx := context.WithValue(context.Background(), "key", "value")  go task(ctx)  time.Sleep(time.Second)  
}  func task(ctx context.Context) {  fmt.Println(ctx.Value("key"))  
}

使用WithVlue的注意事项:

  • 不建议使用context携带关键参数,关键参数应该显示的声明出来,而不是隐式处理。context最好是携带签名、trace_id这类值。
  • 建议key采用内置类型。这是为了避免context因多个包同时使用context而带来冲突。
  • 在获取value时,context会首先从当前ctx中获取,如果没有找到,则会从父级继续查找,直到查找到或者在某个父context中返回nil
  • context传递的key``value键值对是interface类型,因此在类型断言时,要考虑程序的健壮性。

总结

context包在做并发控制上具有相当方便的功能,如在做任务的取消、超时以及传递隐式参数的情境。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词