概述
context包定义了context的类型,该类型在api和进程之间传递deadline,取消信号和其他请求范围的值。
向服务器的传入请求应创建一个上下文,而对服务器的传出调用应接受一个上下文。 它们之间的函数调用链必须传播Context,可以选择将其替换为使用WithCancel,WithDeadline,WithTimeout或WithValue创建的派生Context。 取消上下文后,从该上下文派生的所有上下文也会被取消。
WithCancel,WithDeadline和WithTimeout函数采用Context(父级)并返回派生的Context(子级)和CancelFunc。 调用CancelFunc会取消该子代及其子代,删除父代对该子代的引用,并停止所有关联的计时器。 未能调用CancelFunc会使子代及其子代泄漏,直到父代被取消或计时器触发。 go vet检查所有控制流路径上是否都使用了CancelFuncs。
使用上下文的程序应遵循以下规则,以使各个包之间的接口保持一致,并启用静态分析工具来检查上下文传播:
- 不要将上下文存储在结构类型中; 而是将上下文明确传递给需要它的每个函数。 Context应该是第一个参数,通常命名为ctx:
func DoSomething(ctx context.Context, arg Arg) error {
// ... use ctx ...
}
- 即使函数允许,也不要传递nil Context。 如果不确定使用哪个上下文,请传递context.TODO。
- 仅将上下文值用于传递过程和API的请求范围的数据,而不用于将可选参数传递给函数。
- 可以将相同的上下文传递给在不同goroutine中运行的函数。 上下文对于由多个goroutine同时使用是安全的。
常用函数介绍
1.WithCancel
作用:返回一个父上下文的拷贝,并携带一个Done的管道。不论返回的cancel函数被调用或者当父上下文的Done channel关闭这两种情况哪一个先发生,返回的上下文的Done channel关闭。
取消此context回什邡预期关联的资源,因此在此上下文中运行的操作完成后,代码应立即调用cancel。
此示例演示了使用可取消上下文来防止goroutine泄漏。 在示例函数结束时,由gen启动的goroutine将返回而不会泄漏。
package main
import (
"context"
"fmt"
)
func main() {
// gen generates integers in a separate goroutine and
// sends them to the returned channel.
// The callers of gen need to cancel the context once
// they are done consuming generated integers not to leak
// the internal goroutine started by gen.
gen := func(ctx context.Context) <-chan int {
dst := make(chan int)
n := 1
go func() {
for {
select {
case <-ctx.Done():
return // returning not to leak the goroutine
case dst <- n:
n++
}
}
}()
return dst
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // cancel when we are finished consuming integers
for n := range gen(ctx) {
fmt.Println(n)
if n == 5 {
break
}
}
}
2.WithDeadline
WithDeadline返回父上下文的副本,并将截止日期调整为不迟于d(d是由我们定义的一个时间点,此处是与WithTimeout区别的地方),当父运行时间少于d,则本质上等于父上下文,当返回调用的cancel函数或者关闭父上下文的Done通道在截止时期之后,则关闭返回的上下文的Done通道。
该示例传递一个带有任意截止日期的上下文,以告知阻塞函数它应在到达工作后立即放弃工作。
package main
import (
"context"
"fmt"
"time"
)
func main() {
d := time.Now().Add(50 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), d)
// Even though ctx will be expired, it is good practice to call its
// cancellation function in any case. Failure to do so may keep the
// context and its parent alive longer than necessary.
defer cancel()
select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
}
3.WithTimeout
WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
这个例子让阻塞等待50Millisecond后结束
package main
import (
"context"
"fmt"
"time"
)
func main() {
// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}
}
4.WithValue
WithValue返回父项的副本,其中与键关联的值为val。
仅将上下文值用于传递过程和API的请求范围的数据,而不用于将可选参数传递给函数。
此示例演示如何将值传递给上下文,以及如何将其检索(如果存在)。
提供的密钥必须具有可比性,并且不能为字符串类型或任何其他内置类型,以避免使用上下文在程序包之间发生冲突。 WithValue的用户应定义自己的密钥类型。 为了避免在分配给接口{}时分配,上下文键通常具有具体类型struct {}。 另外,导出的上下文键变量的静态类型应该是指针或接口。
package main
import (
"context"
"fmt"
)
func main() {
type favContextKey string
f := func(ctx context.Context, k favContextKey) {
if v := ctx.Value(k); v != nil {
fmt.Println("found value:", v)
return
}
fmt.Println("key not found:", k)
}
k := favContextKey("language")
ctx := context.WithValue(context.Background(), k, "Go")
f(ctx, k)
f(ctx, favContextKey("color"))
}
context在并发模式中的应用
简介
在Go的服务中,每个传入请求都在自己的goroutine中进行处理。请求处理程序一般会启动另外的goroutine来访问后端,如数据库和RPC服务。这一系列在request运行的goroutine通常需要权限去访问特定的值,比如终端用户的身份,授权令牌,请求期限。当请求被取消或者超时,依赖于该request的所有goroutine应该迅速退出,以便系统可以回收他们正在使用的任何资源。
context包就是用于跨api边界的将request范围的值,取消信号,和截止时间传递给所有涉及处理request的值。该软件包可作为上下文公开使用。
关于context的类型:
// A Context carries a deadline, cancelation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
// Done returns a channel that is closed when this Context is canceled
// or times out.
Done() <-chan struct{}
// Err indicates why this context was canceled, after the Done channel
// is closed.
Err() error
// Deadline returns the time when this Context will be canceled, if any.
Deadline() (deadline time.Time, ok bool)
// Value returns the value associated with key or nil if none.
Value(key interface{}) interface{}
}
上下文对于由多个goroutine同时使用是安全的。 代码可以将单个上下文传递给任意数量的goroutine,并取消该上下文以发出所有信号。
派生上下文
上下文包提供了从现有值派生新的Context值的功能。 这些值形成一棵树:取消上下文后,从该上下文派生的所有上下文也会被取消。
Background 是任何上下文树的根; 它永远不会被取消:
WithCancel和WithTimeout返回派生的Context值,该值可以比父Context早被取消。 请求处理程序返回时,通常会取消与传入请求关联的上下文。 使用多个副本时,WithCancel对于取消冗余请求也很有用。 WithTimeout对于设置对后端服务器的请求的截止日期很有用:
WithValue提供了一种将请求范围的值与Context相关联的方法
参考文章:
https://golang.org/pkg/context/
https://blog.golang.org/context