Detailed explanation of Golang context

藏色散人
Release: 2020-09-10 09:30:29
forward
3990 people have browsed it

The following column golang tutorial will explain the context of Golang in detail. I hope it will be helpful to friends in need!

Detailed explanation of Golang context

#Preface

Yes, today I originally wanted to go out and play. I bought a train ticket and overslept again. . There is no way, maybe it is God's will, so I have to summarize the context of golang, hoping to make a break with the context.

In the company, when we write various services, we must use Context as the first parameter. At first, I thought it was mainly used for full-link troubleshooting and tracking. But with more contact, it turns out it's more than that.

Text

1.Detailed explanation of context

1.1 Generation background

Before go 1.7, context was still non-programmed (including golang.org/x /net/context), the golang team found that context is quite easy to use and is used in many places, so they incorporated it into the standard library

version 1.7. Common usage postures of context:

1. In web programming, one request corresponds to data interaction between multiple goroutines

2. Timeout control
3. Context control

1.2 The underlying structure of context

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
Copy after login

This is the underlying data structure of Context. Let’s analyze it:

FieldDeadlineDoneErrValueAt the same time, the package also defines the interface that needs to be implemented to provide the cancel function. This is mainly because the "cancellation signal and timeout signal" mentioned later need to be implemented.
Meaning
Returns a time.Time, indicating the time when the current Context should end, ok means there is an end time
A close channel returned when the Context is canceled or times out, telling the context-related functions to stop the current work and return. (This is a bit like global broadcast)
The reason why context was canceled
The place where context implements shared data storage is coroutine safe (remember what I said before that map is unsafe? So when you encounter the map structure, if it is not sync.Map, you need to lock it to operate)
// 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{}
}
Copy after login

Then the library provides 4 Context implementations for everyone to play with

ImplementationemptyCtxcancelCtx Context timerCtx cancelCtxcancelCtxvalueCtx Context

1.3 context的创建

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

var (
    background = new(emptyCtx)
    todo       = new(emptyCtx) // 
)

func Background() Context {
    return background
}
Copy after login

你可以认为所有的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) }
}
Copy after login

实战场景:
执行一段代码,控制执行到某个度的时候,整个程序结束。

吃汉堡比赛,奥特曼每秒吃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
}
Copy after login

输出:

我吃了 1 个汉堡
我吃了 3 个汉堡
我吃了 5 个汉堡
我吃了 9 个汉堡
我吃了 10 个汉堡
正在统计结果。。。
耗时 6 秒,吃了 10 个汉堡
Copy after login

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))
}
Copy after login

实战场景:
执行一段代码,控制执行到某个时间的时候,整个程序结束。

吃汉堡比赛,奥特曼每秒吃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)
	}
}
Copy after login

输出:

我吃了 1 个汉堡
我吃了 3 个汉堡
我吃了 5 个汉堡
我吃了 9 个汉堡
我吃了 10 个汉堡
我吃了 13 个汉堡
我吃了 13 个汉堡
我吃了 13 个汉堡
我吃了 14 个汉堡
我吃了 14 个汉堡
stop
Copy after login

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}
}
Copy after login

实战场景:
携带关键信息,为全链路提供线索,比如接入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)
}
Copy after login

输出:

traceID: 88888888 -session: 1
Copy after login

3.context建议

不多就一个。

Context要是全链路函数的第一个参数

func myTest(ctx context.Context)  {
    ...
}
Copy after login

(写好了竟然忘记发送了。。。汗)

The above is the detailed content of Detailed explanation of Golang context. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:csdn.net
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!
StructureFunction
type emptyCtx intCompletely empty Context, the implemented functions also return nil, It just implements the Context interface
type cancelCtx struct { mu sync.Mutex
done chan struct{}

children map[canceler]struct{}
err error
}

Inherits from Context and also implements the canceler interface
type timerCtx struct { timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}


Inherited from
, adding timeout mechanism
type valueCtx struct { key, val interface{}
}

Data that stores key-value pairs