preface

In the web system, every time the network will create a collaborators process or thread to handle the request, in each request basically will be open more than other collaborators ride to request corresponding data resources, that how to such as stop and data transmission between coroutines, the context here will use the below to the context object, In fact, in most languages there is no concept of context.

What is the context?

A Context Cancellation signal, A cancellation signal, and other values across

What does that mean?

Context is a context object with an expiration date, a cancel signal, and a key and value.

How to use

Let’s look at example 1:

func main(a) {
	fmt.Println("main is start")
	go Test("value")
	fmt.Println("main is finish")
	time.Sleep(3 * time.Second)
}

func Test(value string) {
	fmt.Println("test is start")
	time.Sleep(5 * time.Second)
	fmt.Println("test is finish")}Copy the code

Green flowers bear fruit: In example 1, we are creating a coroutine Test, and we use time.Sleep method to simulate the execution time of Test. However, we cannot obtain the state of the subcoroutine in the main coroutine, and we can also know the running state and completion of the main coroutine in the subcoroutine. So the subcoroutine is still running, which is a waste of resources. So this is where context comes in handy.

Sync signal

package main

import (
	"context"
	"fmt"
	"time"
)

func main(a) {
	fmt.Println("main is start")
	ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
	go TestContext(ctx, 1500*time.Millisecond)
	fmt.Println("main is finish")
	select {
	case <-ctx.Done():
		fmt.Println("[main] is deadline", ctx.Err())
	}
}

// Test the Context's unc
func TestContext(context context.Context, timSec time.Duration) {
	fmt.Println("testContext is start")
	select {
	case <-context.Done():
		fmt.Println("[TestContext] is err", context.Err())
	case <-time.After(timSec):
		fmt.Println("[TestContext] is with time ", timSec)
	}
}

Copy the code

Results on green flowers: You can see we’re calling the WithTimeOut method of the Context package inside the main coroutine to set a timeout and return a new Context and we’ll go back to the structure and methods of the Context later. We can see that the child coroutine does not go into the timeout logic, but the main coroutine does go into the select Done() logic because it waits for the timeout. Let’s make a slight change to the subcoroutine timeout to 500 milliseconds,

	go TestContext(ctx, 1500*time.Microsecond)
Copy the code

Green flowers on the picture Here we find that a timeout exception occurs directly in the subcoroutine because the execution time is too long. Similarly, our main coroutine also times out.

The context by value

func main(a) {
	fmt.Println("main is start")
	ctx := context.WithValue(context.Background(), "key"."value")
	go TestValue(ctx)
	time.Sleep(1 * time.Second)
}

func TestValue(ctx context.Context) {
	fmt.Println("= = = = = = = = >", ctx.Value("key"))
	select {
	case <-ctx.Done():
		fmt.Println("[TestContext] and value is ", ctx.Value("key"))}}Copy the code

Green flowers on the picture

We use the withValue() method of the context to create a context and pass in the value. We can see that CTX gets the value of key in the subcoroutine. Let’s tweak the code a little bit

func TestValue(ctx context.Context) {
	fmt.Println("TestValue ========01========", ctx.Value("key"))
	go func(a) {
		fmt.Println("TestValue ========02========", ctx.Value("key"}} ()))Copy the code

Screenshot from Cuihua We create a child coroutine within the child coroutine, and obviously we can see that the CTX key of the two coroutines is the same. And this is actually saying that the context is passing through the coroutine

The source code of centext

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

Context is an interface that contains four methods: DeadLine(), Done(), Err(), and Value(). In fact, we used the following three methods. The Done() return Value is an empty channel, which is executed when context timeout occurs. And while the Err() method prints the corresponding error, the value method actually passes a top-down value. Deadline() returns the time when work has been completed representing that context. Represents the time at which the context needs to be cancelled. Example:

ctx := context.WithValue(context.Background(), "key"."value")
Copy the code

In the above example we use WithValue() to store a key, value, and tell a new context object to return, while passing context.backgroud () to return the context package is the simplest method:

	type emptyCtx int
	background = new(emptyCtx)
}
Copy the code

And the way to do that is never canceled, has no values, and has no deadline.

func TODO(a) Context {
	return todo
}

func Background(a) Context {
	return background
}
Copy the code

All the above functions return methods of the emptyCtx structure.

conclusion

Each Context is passed layer by layer from the top Goroutine to the bottom layer, and this is the most common way Context is used in Golang. Without Context, when something goes wrong at the top, the bottom layer doesn’t actually receive the error and continues to execute.

The last

Live and learn