go的语言中context是我们常用中常说的上下文,context在我们业务中也是经常使用的,其主要的应用1:上下文控制:io处理异常等处理,多个goroutine之间的数据交互等,2:超时控制:到某个时间点超时,过多久超时等应用。
golang的context的数据结构
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
我们简单来看一下他的结构的意思
字段 | 含义 |
---|---|
Deadline | 返回一个time.Time,表示当前Context应该结束的时间,ok则表示有结束时间 |
Done | 当Context被取消或者超时时候返回的一个close的channel,告诉给context相关的函数要停止当前工作然后返回了 |
Err | context被取消的原因 |
Value | context实现共享数据存储的地方,是协程安全的 |
其实整个context的实现还是比较简单的,他的具体怎么实现今天就不讲了,下面我们来看他的功能玩法。
1:context手动去掉的使用
- 根context:通过context.Background()创建
- 子context:context.WithCancel(parentContext)创建
- ctx,cancel:=context.WithCancel(context.Background())
- 当前context被取消时 基于他的子context都会被取消
- 接收取消通知 <-ctx.Done()
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
for i := 0; i < 5; i++ {
go func(i int, ctx context.Context) {
for {
if canceled(ctx) {
break
}
doOneWork()
}
fmt.Println(i, "canceled.")
}(i, ctx)
}
cancel()
time.Sleep(time.Second * 5)
}
func canceled(ctx context.Context) bool {
select {
case <-ctx.Done():
fmt.Println("取消了")
return true
default:
return false
}
}
func doOneWork() {
fmt.Println(111)
}
执行结果(可能执行多次,会有不一样的结果,那属于正常的):
111
取消了
取消了
1 canceled.
取消了
2 canceled.
取消了
4 canceled.
0 canceled.
取消了
3 canceled.
2:看一下超时取消和超过某个时间取消的功能
package main
import (
"context"
"fmt"
"time"
"math/rand"
)
func main() {
//下面是1秒后超时
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
//下面是当前时间+1秒取消
//ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1))
doOneWork(ctx)
defer cancel()
fmt.Println(ctx.Err())
}
func doOneWork(ctx context.Context) {
n := 0
for {
select {
case <-ctx.Done():
fmt.Println("stop \n")
return
default:
incr := rand.Intn(5)
n += incr
fmt.Println(incr)
}
}
}
上面的代码,大家去执行,我就不复制结果了,每次执行结果不尽相同。
3:看一下context写到数据到子的context的使用
package main
import (
"context"
"fmt"
)
func main() {
ctx := context.WithValue(context.Background(), "trace_id", "88888888")
// 携带user_id到后面的程序中去
ctx = context.WithValue(ctx, "user_id", 1)
process(ctx)
}
func process(ctx context.Context) {
userId, ok := ctx.Value("user_id").(int)
if !ok {
fmt.Println("something wrong")
return
}
if userId <= 0 {
fmt.Println("userId 存在")
return
}
traceID := ctx.Value("trace_id").(string)
fmt.Println("traceID:", traceID, "\nuserId:", userId)
}
下面我们来看一下结果
traceID: 88888888
userId: 1
context使用我们就讲到这里了,我们业务中可能经常会用到context,学会context是极其重要的。