context包定义了 Context 接口,Context 的具体实现包括 4 个方法,分别是Deadline、Done、Err 和 Value,如下所示:
GO语言的Context_一般函数
下面来具体解释下这 4 个方法。
 
Deadline 方法会返回这个 Context 被取消的截止日期。如果没有设置截止日期,ok 的值是 false。后续每次调用这个对象的 Deadline 方法时,都会返回和第一次调用相同的结果。
 
Done 方法返回一个 Channel 对象。在 Context 被取消时,此 Channel 会被 close,如果没被取消,可能会返回 nil。后续的 Done 调用总是返回相同的结果。当 Done 被 close的时候,你可以通过 ctx.Err 获取错误信息。Done 这个方法名其实起得并不好,因为名字太过笼统,不能明确反映 Done 被 close 的原因,因为 cancel、timeout、deadline 都可能导致 Done 被 close,不过,目前还没有一个更合适的方法名称。
 
关于 Done 方法,你必须要记住的知识点就是:如果 Done 没有被 close,Err 方法返回nil;如果 Done 被 close,Err 方法会返回 Done 被 close 的原因。
 
Value 返回此 ctx 中和指定的 key 相关联的 value。
 
Context 中实现了 2 个常用的生成顶层 Context 的方法。
context.Background():返回一个非 nil 的、空的 Context,没有任何值,不会被cancel,不会超时,没有截止日期。一般用在主函数、初始化、测试以及创建根Context 的时候。
context.TODO():返回一个非 nil 的、空的 Context,没有任何值,不会被 cancel,不会超时,没有截止日期。当你不清楚是否该用 Context,或者目前还不知道要传递一些什么上下文信息的时候,就可以使用这个方法。
官方文档是这么讲的,你可能会觉得像没说一样,因为界限并不是很明显。其实,你根本不用费脑子去考虑,可以直接使用 context.Background。事实上,它们两个底层的实现是一模一样的:
GO语言的Context_初始化_02

 

 

在使用 Context 的时候,有一些约定俗成的规则。
1. 一般函数使用 Context 的时候,会把这个参数放在第一个参数的位置。从来不把 nil 当做 Context 类型的参数值,可以使用 context.Background() 创建一个空的上下文对象,也不要使用 nil。
2.Context 只用来临时做函数之间的上下文透传,不能持久化 Context 或者把 Context长久保存。把 Context 持久化到数据库、本地文件或者全局变量、缓存中都是错误的用法。
3.key 的类型不应该是字符串类型或者其它内建类型,否则容易在包之间使用 Context 时候产生冲突。使用 WithValue 时,key 的类型应该是自己定义的类型。
4.常常使用 struct{}作为底层类型定义 key 的类型。对于 exported key 的静态类型,常常是接口或者指针。这样可以尽量减少内存分配。