Use of context in golang

background

(Welcome to pay attention to the "Cloud Native Notes" WeChat public
account ) Three implementations of concurrent programming in golang: chan pipeline, waitGroup and Context. This article will focus on the use of context, and tell you the basic way to use it.

Context

Concept introduction

Insert picture description here

Context is translated into context. Golang did not have its own context at the time of 1.6.2. In version 1.7, the golang.org/x/net/context package was added to the official library. The Context package of golang is specifically used to process data between multiple goroutines and request domains, cancel signals, deadlines and other related operations.
Usage scenario: When a request comes, you need to use multiple sub-coroutines to process the data, but at this time the business reports an error, you need to cancel the sub-coroutine and return the request, directly returning regardless of the sub-coroutine may cause dirty data , And the sub-coroutine may occupy system resources, so you need to close the sub-coroutine. The context can provide the function that the parent coroutine can close the child coroutine when the child coroutine is running.
The presentation of context is like a binary tree structure, with a parent-child relationship, and the parent coroutine manages the child coroutine. The usage scenario of context is that the main coroutine manages multiple sub-coroutines, and the management here is to simply and rudely close the sub-coroutines. There are three ways to close the coroutine rudely: context.WithCancel method, context.WithTimeout method and context.WithDeadline method, including active and passive closing methods. Below I give an example of the use of these methods.

Examples of common methods

context.WithCancel method

The following is the use of context.WithCancel method to generate the current coroutine context ctx and cancel functions, the cancellation function CancelFunc, which is a function type, its definition is very simple as follows:

type CancelFunc func()

, And then pass ctx to the sub-coroutine managed by the current coroutine, and then manage the sub-coroutine through the cancel function (the main management method is to forcibly close the sub-coroutine), as follows:

func CancelTest() {
    
    
	ctx, cancel := context.WithCancel(context.Background())
	go func(ctx context.Context) {
    
    
		for {
    
    
			select {
    
    
			case <-ctx.Done():
				fmt.Println("协程退出,停止了...")
				return
			default:
				fmt.Println("协程运行中...")
				time.Sleep(2 * time.Second)
			}
		}
	}(ctx)
	time.Sleep( time.Second * 30)
	fmt.Println("两分钟时间到了,关闭子协程")
	cancel()
	time.Sleep( time.Second * 10)
	fmt.Println("演示结束")
}

The above program will be closed by the main coroutine after the sub coroutine runs for 30s.

context.WithTimeout method

The context.WithTimeout method returns the context ctx and cancel functions. Compared with WithCancel, the parameters of this WithTimeout method have more time parameters, that is, you can set a time, and the cancel function will be triggered actively after this time. The trigger principle is the timer Timer, of course You can also use the cancel function to trigger:

func TimeOutTest() {
    
    
	ctx, _ := context.WithTimeout(context.Background(), time.Minute * 1)
	go func(ctx context.Context) {
    
    
		for {
    
    
			select {
    
    
			case <-ctx.Done():
				fmt.Println("协程退出,停止了...")
				return
			default:
				fmt.Println("协程运行中...")
				time.Sleep(2 * time.Second)
			}
		}
	}(ctx)
	time.Sleep(time.Second * 70)
	fmt.Println("演示结束")
}

In the above function, the sub-coroutine will voluntarily exit after running for 1 minute, and then the main coroutine will also exit after 10 seconds. You can also use the cancel function to actively call the sub-coroutine before 1 minute has passed.

context.WithDeadline method

The functions of the two methods context.WithDeadline and context.WithTimeout are similar. The difference is that WithDeadline can call the cancel function at any specified time, while WithTimeout can only call the cancel function based on the time of the call and a period of time.

func DeadLineTest() {
    
    
	ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Minute * 1))
	go func(ctx context.Context) {
    
    
		for {
    
    
			select {
    
    
			case <-ctx.Done():
				fmt.Println("协程退出,停止了...")
				return
			default:
				fmt.Println("协程运行中...")
				time.Sleep(2 * time.Second)
			}
		}
	}(ctx)
	time.Sleep(time.Second * 70)
	fmt.Println("演示结束")
}

In the above function, context.WithDeadline needs to be passed in a time point. The time point I wrote is time.Now().Add(time.Minute * 1). The cancel function is triggered one minute after the current time (you can choose any time Point), so the sub-coroutine will finish running after one minute. Of course, you can actively call the cancel function returned by the withDeadline method to manage the sub-coroutine in advance.

context.WithValue method

The WithValue function returns only the context context, and the incoming value has a key-value key-value pair. This key-value pair can be accessed in the sub-coroutine using the Context.Value method, as follows:

func WithValueTest() {
    
    
	ctx, cancel := context.WithCancel(context.Background())
	//附加值
	valueCtx := context.WithValue(ctx,"test","子协程1")
	go func(ctx context.Context) {
    
    
		for {
    
    
			select {
    
    
			case <-ctx.Done():
				//取出值
				fmt.Println(ctx.Value("test"), "监控退出,停止了...")
				return
			default:
				//取出值
				fmt.Println(ctx.Value("test"), "goroutine监控中...")
				time.Sleep(2 * time.Second)
			}
		}
	}(valueCtx)
	time.Sleep(10 * time.Second)
	fmt.Println("可以了,通知监控停止")
	cancel()
	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
	time.Sleep(5 * time.Second)
}

Pass the configured key-value context to the child coroutine, and then the child coroutine can use the context.value() method to access the value, which is more convenient to pass the value in each parent-child coroutine.
####Context Usage Rules
Briefly introduce the context usage rules to avoid misuse:

  • context should be passed as a function parameter, not a field of struct
  • context should be the first parameter of the function
  • Do not pass nil context, even if it is not used, if you don’t know what to use, use context.TODO
  • Only use context to pass the request context, not to pass optional parameters
  • Context may be used by the same function in different goroutines, it is concurrently safe

Context defect

Although the context realizes the management of the parent coroutine to the child coroutine, this management method is relatively rude, and it is closed directly, and the execution result of the child coroutine is not known when it is closed. In short, the management of the sub-coroutine is not detailed enough. If necessary, you need to use defer to exit when the word coroutine exits, or you can use waitGroup to have a clear understanding of the execution result of the coroutine.

to sum up

This article mainly explains the use of golang context in coroutine management. It is just the basics. You only need to remember that context can be thought of when encountering the scenario of parent coroutine managing child coroutine. Just have an understanding of the functions of the four basic methods: context.WithCancel method, context.WithTimeout method, context.WithDeadline method, and context.WithValue method.

Guess you like

Origin blog.csdn.net/u013276277/article/details/108923912