您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

详解Golang的context

2025/2/5 5:26:29发布27次查看
下面由golang教程栏目给大家详解golang的context,希望对需要的朋友有所帮助!
前言是的,今天本来还想出去玩的。买了动车票,然后又睡过头了。。没办法,可能是天意,只好总结一下golang的context,希望能与context之间做一个了断。
公司里头大家写各种服务,必须需要将context作为第一个参数,刚开始以为主要用于全链路排查跟踪。但是随着接触多了,原来不止于此。
正文1.context详解1.1 产生背景在go的1.7之前,context还是非编制的(包golang.org/x/net/context中),golang团队发现context这个东西还挺好用的,很多地方也都用到了,就把它收编了,1.7版本正式进入标准库。
context常用的使用姿势:
1.web编程中,一个请求对应多个goroutine之间的数据交互
2.超时控制
3.上下文控制
1.2 context的底层结构type context interface { deadline() (deadline time.time, ok bool) done() <-chan struct{} err() error value(key interface{}) interface{}}
这个就是context的底层数据结构,来分析下:
字段含义
deadline 返回一个time.time,表示当前context应该结束的时间,ok则表示有结束时间
done 当context被取消或者超时时候返回的一个close的channel,告诉给context相关的函数要停止当前工作然后返回了。(这个有点像全局广播)
err context被取消的原因
value context实现共享数据存储的地方,是协程安全的(还记得之前有说过map是不安全的?所以遇到map的结构,如果不是sync.map,需要加锁来进行操作)
同时包中也定义了提供cancel功能需要实现的接口。这个主要是后文会提到的“取消信号、超时信号”需要去实现。
// a canceler is a context type that can be canceled directly. the// implementations are *cancelctx and *timerctx.type canceler interface { cancel(removefromparent bool, err error) done() <-chan struct{}}
那么库里头提供了4个context实现,来供大家玩耍
实现结构体作用
emptyctx type emptyctx int 完全空的context,实现的函数也都是返回nil,仅仅只是实现了context的接口
cancelctx type cancelctx struct {
context
mu sync.mutex
done chan struct{}
children map[canceler]struct{}
err error
} 继承自context,同时也实现了canceler接口
timerctx type timerctx struct {
cancelctx
timer *time.timer // under cancelctx.mu.
deadline time.time
}
继承自cancelctx,增加了timeout机制
valuectx type valuectx struct {
context
key, val interface{}
} 存储键值对的数据
1.3 context的创建为了更方便的创建context,包里头定义了background来作为所有context的根,它是一个emptyctx的实例。
var ( background = new(emptyctx) todo = new(emptyctx) // )func background() context { return background}
你可以认为所有的context是树的结构,background是树的根,当任一context被取消的时候,那么继承它的context 都将被回收。
2.context实战应用2.1 withcancel实现源码:
func withcancel(parent context) (ctx context, cancel cancelfunc) { c := newcancelctx(parent) propagatecancel(parent, &c) return &c, func() { c.cancel(true, canceled) }}
实战场景:
执行一段代码,控制执行到某个度的时候,整个程序结束。
吃汉堡比赛,奥特曼每秒吃0-5个,计算吃到10的用时
实战代码:
func main() { ctx, cancel := context.withcancel(context.background()) eatnum := chihanbao(ctx) for n := range eatnum { if n >= 10 { cancel() break } } fmt.println("正在统计结果。。。") time.sleep(1 * time.second)}func chihanbao(ctx context.context) <-chan int { c := make(chan int) // 个数 n := 0 // 时间 t := 0 go func() { for { //time.sleep(time.second) select { case <-ctx.done(): fmt.printf("耗时 %d 秒,吃了 %d 个汉堡 \n", t, n) return case c <- n: incr := rand.intn(5) n += incr if n >= 10 { n = 10 } t++ fmt.printf("我吃了 %d 个汉堡\n", n) } } }() return c}
输出:
我吃了 1 个汉堡我吃了 3 个汉堡我吃了 5 个汉堡我吃了 9 个汉堡我吃了 10 个汉堡正在统计结果。。。耗时 6 秒,吃了 10 个汉堡
2.2 withdeadline & withtimeout实现源码:
func withdeadline(parent context, d time.time) (context, cancelfunc) { if cur, ok := parent.deadline(); ok && cur.before(d) { // the current deadline is already sooner than the new one. return withcancel(parent) } c := &timerctx{ cancelctx: newcancelctx(parent), deadline: d, } propagatecancel(parent, c) dur := time.until(d) if dur <= 0 { c.cancel(true, deadlineexceeded) // deadline has already passed return c, func() { c.cancel(true, canceled) } } c.mu.lock() defer c.mu.unlock() if c.err == nil { c.timer = time.afterfunc(dur, func() { c.cancel(true, deadlineexceeded) }) } return c, func() { c.cancel(true, canceled) }}func withtimeout(parent context, timeout time.duration) (context, cancelfunc) { return withdeadline(parent, time.now().add(timeout))}
实战场景:
执行一段代码,控制执行到某个时间的时候,整个程序结束。
吃汉堡比赛,奥特曼每秒吃0-5个,用时10秒,可以吃多少个
实战代码:
func main() { // ctx, cancel := context.withdeadline(context.background(), time.now().add(10)) ctx, cancel := context.withtimeout(context.background(), 10*time.second) chihanbao(ctx) defer cancel()}func chihanbao(ctx context.context) { n := 0 for { select { case <-ctx.done(): fmt.println("stop \n") return default: incr := rand.intn(5) n += incr fmt.printf("我吃了 %d 个汉堡\n", n) } time.sleep(time.second) }}
输出:
我吃了 1 个汉堡我吃了 3 个汉堡我吃了 5 个汉堡我吃了 9 个汉堡我吃了 10 个汉堡我吃了 13 个汉堡我吃了 13 个汉堡我吃了 13 个汉堡我吃了 14 个汉堡我吃了 14 个汉堡stop
2.3 withvalue实现源码:
func withvalue(parent context, key, val interface{}) context { if key == nil { panic("nil key") } if !reflect.typeof(key).comparable() { panic("key is not comparable") } return &valuectx{parent, key, val}}
实战场景:
携带关键信息,为全链路提供线索,比如接入elk等系统,需要来一个trace_id,那withvalue就非常适合做这个事。
实战代码:
func main() { ctx := context.withvalue(context.background(), "trace_id", "88888888") // 携带session到后面的程序中去 ctx = context.withvalue(ctx, "session", 1) process(ctx)}func process(ctx context.context) { session, ok := ctx.value("session").(int) fmt.println(ok) if !ok { fmt.println("something wrong") return } if session != 1 { fmt.println("session 未通过") return } traceid := ctx.value("trace_id").(string) fmt.println("traceid:", traceid, "-session:", session)}
输出:
traceid: 88888888 -session: 1
3.context建议不多就一个。
context要是全链路函数的第一个参数。
func mytest(ctx context.context) { ...}
(写好了竟然忘记发送了。。。汗)
以上就是详解golang的context的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product