笔记 Go context 包使用
context包在Go并发编程代码中经常可见,而在我学习Go的两本书中《Go圣经》《Go实战》都没有看到这个包的介绍,才知道是Go1.7版本中才将其放入到标准库中。
在Go服务中,一个请求过来,我们给分配一个gor处理,在这个请求里,通常还会继续开启gor来访问其他后端服务。而当这个请求结束的时候,我们必须要终止这些额外开启的gor,以免gor泄露。context包正是为了解决这个问题。
Context接口类型
type Context interface {
// 当Context被取消或者超时,Done()返回一个关闭状态的channel
Done() <-chan struct{}
// 表示这个Context为什么被取消
Err() error
// 返回这个context什么时候被取消
Deadline() (deadline time.Time, ok bool)
// 返回这个key对应的value 没有则是nil
Value(key interface{}) interface{}
}
- Done()方法返回关闭状态的channel,就表示函数该结束并返回了
- Context是并发安全的,可以在多个gor中传递。
- Value允许Context在一个请求域范围下绑定数据,这个数据要求并发安全,通常绑定请求id如request id。
Context派生
Context是以树形结构生长,获取一个新的Context需要依赖一个存在的Context,当一个Context被取消后,它派生的其他Context也会被取消。
Background是Context树的根,顶层Context。
// Background returns an empty Context. It is never canceled, has no deadline,
// and has no values. Background is typically used in main, init, and tests,
// and as the top-level Context for incoming requests.
func Background() Context
返回一个空的Context,不会被取消,没有deadline,没有values。Background通常用于main、init、test作为顶层Context。
WithCancel 和 WithTimeout 返回派生的Context。
// WithCancel returns a copy of parent whose Done channel is closed as soon as
// parent.Done is closed or cancel is called.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
// A CancelFunc cancels a Context.
type CancelFunc func()
// WithTimeout returns a copy of parent whose Done channel is closed as soon as
// parent.Done is closed, cancel is called, or timeout elapses. The new
// Context's Deadline is the sooner of now+timeout and the parent's deadline, if
// any. If the timer is still running, the cancel function releases its
// resources.
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
WithValue
// WithValue returns a copy of parent whose Value method returns val for key.
func WithValue(parent Context, key interface{}, val interface{}) Context
example
go标准库中的例子,加深理解
WithCancel
利用Context通知产生数据的gor停止,避免gor泄露
// This example demonstrates the use of a cancelable context to prevent a
// goroutine leak. By the end of the example function, the goroutine started
// by gen will return without leaking.
func ExampleWithCancel() {
// 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
}
}
// Output:
// 1
// 2
// 3
// 4
// 5
}
WithDeadline
// This example passes a context with an arbitrary deadline to tell a blocking
// function that it should abandon its work as soon as it gets to it.
func ExampleWithDeadline() {
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())
}
// Output:
// context deadline exceeded
}
WithTimeout
// This example passes a context with a timeout to tell a blocking function that
// it should abandon its work after the timeout elapses.
func ExampleWithTimeout() {
// 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"
}
// Output:
// context deadline exceeded
}
WithValue
// This example demonstrates how a value can be passed to the context
// and also how to retrieve it if it exists.
func ExampleWithValue() {
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"))
// Output:
// found value: Go
// key not found: color
}
参考: