Golang——Context
context.Background() Context
这个函数返回一个空 context。这只适用于高等级(在 main 或顶级请求处理中)。这能用于派生我们稍后谈及的其他 context 。
ctx := context.Background()
context.TODO() Context
这个函数也是创建一个空 context。也只能用于高等级或当您不确定使用什么 context,或函数以后会更新以便接收一个 context 。这意味您(或维护者)计划将来要添加 context 到函数。
ctx := context.TODO()
context.WithValue(parent Context, key, val interface{}) (ctx Context, cancel CancelFunc)
此函数接收 context 并返回派生 context,其中值 val 与 key 关联,并通过 context 树与 context 一起传递。这意味着一旦获得带有值的 context,从中派生的任何 context 都会获得此值。
ctx := context.WithValue(context.Background(), "key", "value")
context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)
这是它开始变得有趣的地方。此函数创建从传入的父 context 派生的新 context。
返回派生 context 和取消函数。只有创建它的函数才能调用取消函数来取消此 context。如果您愿意,可以传递取消函数,但是,强烈建议不要这样做。这可能导致取消函数的调用者没有意识到取消 context 的下游影响。可能存在源自此 context 的其他 context,这可能导致程序以意外的方式运行。简而言之,永远不要传递取消函数。
ctx, cancel := context.WithCancel(context.Background()) // 不要传递 cancel 给其他函数
context.WithTimeout(parent Context, timeout time.Duration) (ctx Context, cancel CancelFunc)
此函数类似于 context.WithDeadline。不同之处在于它将持续时间作为参数输入而不是时间对象。此函数返回派生 context,如果调用取消函数或超出超时持续时间,则会取消该派生 context。
ctx, cancel := context.WithTimeout(context.Background(), 2 * time.Second) // 同样不要传递 cancel
父子 context
package main
import (
"context"
"fmt"
"time"
)
func main() {
ch := make(chan bool, 1) // ch can close main
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
fmt.Println("main: case ctx.Done!!!")
return
case <-ch:
cancel()
}
}
}()
doSomething(ctx, ch)
time.Sleep(100 * time.Millisecond) // 睡眠100ms 为了让main函数不立即结束
}
func doSomething(ctx context.Context, ch chan bool) {
localCtx, _ := context.WithCancel(ctx)
// do...
// if something wrong need to close main
ch <- true
go func() {
select {
case <-localCtx.Done():
fmt.Println("doSomething: case ctx.Done!!!")
}
}()
} 输出:
doSomething: case ctx.Done!!! main: case ctx.Done!!!
说明:父 context 可以 cancel 掉子 context
package main
import (
"context"
"fmt"
"time"
)
func main() {
ch := make(chan bool, 1) // ch can close main
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
fmt.Println("main: case ctx.Done!!!")
return
case <-ch:
cancel()
}
}
}()
doSomething(ctx)
time.Sleep(100 * time.Millisecond) // 睡眠100ms 为了让main函数不立即结束
}
func doSomething(ctx context.Context) {
localCtx, cancel := context.WithCancel(ctx)
// do...
// if something wrong need to close main
cancel()
go func() {
select {
case <-localCtx.Done():
fmt.Println("doSomething: case ctx.Done!!!")
}
}()
}输出:
doSomething: case ctx.Done!!!
说明: 子 context 不可以 cancel 父 context
超时请求
package main
import (
"context"
"fmt"
"time"
)
func main() {
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"
}
}
查看3道真题和解析