Go语言之Context

本文主要简单介绍了Go语言(golang)中的context。通过对底层结构的分析和实例讲述了context的基本用法。

更多文章欢迎访问我的个人博客–>幻境云图

1. 概述

1.1 作用

  • 1.web编程中,一个请求对应多个goroutine之间的数据交互
  • 2.超时控制
  • 3.上下文控制

1.2 底层结构

1
2
3
4
5
6
7
8
9
10
type Context interface {
//返回一个time.Time,表示当前Context应该结束的时间,ok则表示有结束时间
Deadline() (deadline time.Time, ok bool)
//当Context被取消或者超时时候返回的一个close的channel,告诉给context相关的函数要停止当前工作然后返回了。(这个有点像全局广播)
Done() <-chan struct{}
//context被取消的原因
Err() error
//ntext实现共享数据存储的地方,是协程安全的
Value(key interface{}) interface{}
}

同时包中也定义了提供cancel功能需要实现的接口。这个主要是后文会提到的“取消信号、超时信号”需要去实现。

1
2
3
4
5
6
// 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{}
}

1.3 context的创建

为了更方便的创建Context,包里头定义了Background来作为所有Context的根,它是一个emptyCtx的实例。

1
2
3
4
5
6
7
8
var (
background = new(emptyCtx)
todo = new(emptyCtx) //
)

func Background() Context {
return background
}

你可以认为所有的Context是树的结构,Background是树的根,当任一Context被取消的时候,那么继承它的Context 都将被回收

2. context实战应用

2.1 WithCancel

可以手动取消的 Context

1
2
3
4
5
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func main(){
ctx, cancel := context.WithCancel(context.Background())
add := CountAddCancel(ctx)
for value := range add {
//当累加超过30时 手动调用cancel() 取消context
if value > 30 {
cancel()
break
}
}
fmt.Println("正在统计结果。。。")
time.Sleep(1500 * time.Millisecond)
}

func CountAddCancel(ctx context.Context) <-chan int {
c := make(chan int)
n := 0
t := 0
go func() {
for {
time.Sleep(time.Second * 1)
select {
//手动调用cancel() 取消context 后 channel被close
case <-ctx.Done():
fmt.Printf("耗时 %d S 累加值 % d \n", t, n)
return
case c <- n:
// 随机增加1-5
incr := rand.Intn(4) + 1
n += incr
t++
fmt.Printf("当前累加值 %d \n", n)
}
}
}()
return c
}

2.2 WithDeadline & WithTimeout

设定超时时间,时间到了自动取消context。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func main(){
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
CountAddTimeOut(ctx)
defer cancel()
}

func CountAddTimeOut(ctx context.Context) {
n := 0
for {
select {
case <-ctx.Done():
fmt.Println("时间到了 \n")
return
default:
incr := rand.Intn(4)+1
n += incr
fmt.Printf("当前累加值 %d \n", n)
}
time.Sleep(time.Second)
}
}

2.3 WithValue

可以传递数据的context,携带关键信息,为全链路提供线索,比如接入elk等系统,需要来一个trace_id,那WithValue就非常适合做这个事。

1
2
3
4
5
6
7
8
9
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}
}

3. 建议

  • 1.不要把Context放在结构体中,要以参数的方式传递,parent Context一般为Background
  • 2。应该要把Context作为第一个参数传递给入口请求和出口请求链路上的每一个函数,放在第一位,变量名建议都统一,如ctx。
  • 3.给一个函数方法传递Context的时候,不要传递nil,否则在tarce追踪的时候,就会断了连接
  • 4.Context的Value相关方法应该传递必须的数据,不要什么数据都使用这个传递
  • 5.Context是线程安全的,可以放心的在多个goroutine中传递
  • 6.可以把一个 Context 对象传递给任意个数的 gorotuine,对它执行 取消 操作时,所有 goroutine 都会接收到取消信号。

4. 参考

https://blog.csdn.net/qq_36183935/article/details/81137834

https://blog.csdn.net/u011957758/article/details/82948750

https://www.jianshu.com/p/e5df3cd0708b

------------------本文到此结束感谢您的阅读------------------
0%