preface

Go1.7 introduces the Context package, which defines a variety of contexts, including active cancelable contexts, contexts with expiration or timeout, and contexts with value propagation

The context package was introduced to make it easier for information (such as user information) to be passed between coroutines and to control the exit timing of a set of coroutines

Let’s say we have a function gen that takes context as an argument, creates a bufferless channel, starts a coroutine to stuff one signal into that channel every second, closes the channel when the context ends, and exits normally

func gen(ctx context.Context) chan struct{} {
	c := make(chan struct{})
	go func(a) {
		for {
			select {
			case <-ctx.Done(): / / [1] the context
				fmt.Println("receive context done signal, closing channel now")
				close(c) // Close the channel
				return // Groutine exits normally
			default:
				c <- struct{} {}// [2] Use empty structs to save memory
			}
			time.Sleep(time.Second)
		}
	}()
	return c
}
Copy the code

Next, take a look at a few simple examples of the various types of context provided by the Context package

Example 1: Active cancelable context

Using the context package WithCancel function, you can create a context that supports active cancellation

func UseCancelContext(a) {
	ctx, cancelFunc := context.WithCancel(context.Background())
	go func(a) {
		time.AfterFunc(time.Second*5.func(a) {
      // [1] Cancels the context after five seconds
			cancelFunc()
			fmt.Println("send context done signal")})} ()for range gen(ctx) {
		fmt.Println("receive signal from another goroutine")
	}
	fmt.Println("main goroutine is finished")}Copy the code

[1] cancelFunc cancelFunc cancelFunc cancelFunc cancelFunc cancelFunc cancelFunc cancelFunc cancelFunc cancelFunc cancelFunc cancelFunc cancelFunc Exit coroutines

The result of the function execution is as follows. It can be seen that the coroutine in the gen function closes the channel and exits normally after sending five signals

Example 2: Context with a deadline

Using the context package WithDeadline function, you can create a context with an expiration date

func UseDeadlineContext(a) {
  // Create a context with a deadline of five seconds
	ctx, cancelFunc := context.WithDeadline(context.Background(), time.Now().Add(time.Second*5))
	defer cancelFunc() // [1] Think about why you do this.
	for range gen(ctx) {
		fmt.Println("receive signal from another goroutine")
	}
	fmt.Println("main goroutine is finished")}Copy the code

The above function creates a context with a cutoff time of five seconds after which the channel is closed

Example 3: Context with timeout

Using the context package WithTimeout function, you can create a context with a timeout

func UseTimeoutContext(a) {
	ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)
	defer cancelFunc()
	for range gen(ctx) {
		fmt.Println("receive signal from another goroutine")
	}
	fmt.Println("main goroutine is finished")}Copy the code

The above function creates a context with a five-second timeout, and the code executes as a result

Example 4: Value-propagated context

Using the context package WithValue function, you can create a context with a value propagated

func UseValueContext(a) {
  // Create a context with values, key-value pairs <1,"value">
	ctx := context.WithValue(context.Background(), 1."value")
	c := make(chan struct{})
	go func(a) {
		time.Sleep(time.Second * 1)
    // Get the corresponding value from the context
		value := ctx.Value(1)
		fmt.Printf("get value from ctx, value = %s\n", value)
		c <- struct{}{}
	}()
	<-c
	fmt.Println("main goroutine is finished")}Copy the code

The execution result

conclusion

There are many scenarios in which the context package is used in daily development. Here are a few examples:

  1. Get user information from the request context
  2. Get the unique identity of the request from the request context (traceId, often used for distributed log tracing)
  3. Controls the request timeout period

As you can see, the context package makes it easier to propagate information between a set of coroutines and better control the exit timing of multiple coroutines

Go has also rewritten a number of standard libraries to support context packages

The Go official recommends that the context should not be embedded in the structure, but should be used as the first parameter to call a function/method, and that the parameter be named CTX

/ / do not recommend
type ModuleContext struct {
  c context.Context
}
/ / recommend
func func1(ctx context.Context, opts ... Option)
Copy the code

It is necessary to master the use of this package and understand how it works

In the next article I will address the questions left in this article (in Example 2 [1]) and dig further into the context package source code