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" } }