How do coroutines exit

When a coroutine is started, it usually exits automatically after the code has finished executing, but what if it needs to be terminated early? One approach is to define a global variable, which is checked for changes in the coroutine to determine whether to exit. This method requires a lock to ensure concurrency security. At this point, do you have any solutions in mind? Select + channel to implement:

package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup stopWk := make(chan bool) wg.Add(1) go Func () {defer wg.done () worker(stopWk)}() time.sleep (3* time.second) // stopWk < -true wg.wait ()} func Worker (stopWk Chan bool){for {select {case < -stopWk: fmt.println (" stopWk ~~~") return default: FMT.Println(" Carefully touch fish, do not disturb..." ) } time.Sleep(1*time.Second) } }

Running results:

Carefully touch the fish, do not disturb... Carefully touch the fish, do not disturb... Carefully touch the fish, do not disturb... Go off work to cough up ~ ~ ~

As you can see, once a second it prints out the words “Please do not disturb the fish.” “, 3 seconds later issued a stop command, the program into the “off duty role ~~~”.

At the beginning of the Context experience

We used SELECT + CHANNEL to terminate coroutines, but what if we want to cancel multiple coroutines at the same time? What if I need to cancel regularly? At this point, the Context needs to come into play, which can track each coroutine. Let’s rewrite the above example:

package main import ( "context" "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup ctx, stop := context.WithCancel(context.Background()) wg.Add(1) go func() { defer wg.Done() worker(ctx) }() Wg.wait ()} func worker(CTX context.context){for {select {case <- Ctx. Done(): FMT.Println(" ") return default: FMT.Println(" ") ) } time.Sleep(1*time.Second) } }

Running results:

Carefully touch the fish, do not disturb... Carefully touch the fish, do not disturb... Carefully touch the fish, do not disturb... Go off work to cough up ~ ~ ~

The Context is introduced

Context is concurrency safe, it is an interface, can manually, timing, timeout cancel signal, pass value and other functions, is mainly used to control the collaboration between multiple coroutines, cancel operations.

The Context interface has four methods:

type Context interface {
   Deadline() (deadline time.Time, ok bool)
   Done() <-chan struct{}
   Err() error
   Value(key interface{}) interface{}
}
  • The “Deadline” method can get the set Deadline. The return value “Deadline” is the Deadline. When the Deadline is reached, the Context will automatically issue a cancellation request.
  • Done method: Returns a read-only channel of type struct{}. If this CHAN can be read, it means that the cancel signal has been sent and the coroutine can be cleaned up. Then, the coroutine can exit and the resource can be released.
  • The Err method: Returns the reason why the Context was canceled.
  • The Value method gets the Value bound to the Context, which is a key-value pair. The key is used to get the corresponding Value.

The most common one is the Done method, which closes the read-only Channel when the Context is canceled, signaling cancellation.

The Context tree

We don’t need to implement the Context interface ourselves. The Go language provides functions to generate different contexts. Using these functions, we can generate a tree of contexts so that the contexts can be associated. The child Context is also emitted so that coroutine exits at different levels can be controlled.

Generate the root node

  1. emptyCtxIs a variable of type int, but implements the interface to the context.emptyCtxThere’s no timeout, you can’t cancel, you can’t store any extra information, soemptyCtxUsed as the root node of the context tree.
  2. But we don’t usually use them directlyemptyCtx, but use byemptyCtxThe two variables instantiated (background, todo) are called, respectivelyBackgroundandTODOMethod, but the implementation of these two contexts is the same.

The difference between Background and TODO:


Backgroundand
TODOJust for different scenarios:
BackgroundUsually used in main functions, initializations, and tests, as a top-level
contextThat is to say, in general we create
contextAre based on
Background; while
TODOAre you unsure of what to use
contextWhen you use it.

The spanning tree function

  1. You can use the context. Background() gets a root Context.
  2. Once you have the root node, use the following four functions to generate the Context tree:
  3. WithCancel(parent Context) : Generates a cancellable Context.
  4. WithDeadline(parent Context, d time. time) : Generates a timeable cancelable Context with d as the specific time of the cancellation.
  5. WithTimeout(parent Context, timeout time.duration) : Generates a timeout cancelable Context with the timeout parameter setting how long to cancel
  6. WithValue(parent Context, key, val interface{}) : generates a Context that contains key-value pairs.

Context cancels multiple coroutines

If a Context has child contexts, when that Context is canceled, all the child contexts under it will be canceled.

The Context by value

The Context can not only emit a cancel signal, it can also pass a value, and it can make the value it stores available to other coroutines.

Example:

package main import ( "context" "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup ctx, stop := context.WithCancel(context.Background()) valCtx := context.WithValue(ctx, "position","gopher") wg.Add(2) go func() { defer wg.Done() worker(valCtx, }() go func() {defer wg.Done() worker(valCtx, defer wg. Wg.Wait()} func worker(valCtx context.Context, Name string){for {select (case < -valctx. Done(): fmt.println (") ") return default: Position := valctx.value ("position") fmt.println (name,position," ) } time.Sleep(1*time.Second) } }

Running results:

Worker 2 Gopher carefully touch the fish, do not disturb... Worker 1 Gopher carefully touch the fish, do not disturb... Worker 1 Gopher carefully touch the fish, do not disturb... Worker 2 Gopher carefully touch the fish, do not disturb... Worker 2 Gopher carefully touch the fish, do not disturb... Worker 1 Gopher carefully touch the fish, do not disturb... Off duty, off duty, off duty

Context Use Principles

  • The Context should not be placed in a structure and should be passed as an argument
  • When the Context is an argument to the function, it’s going to come first, as the first argument
  • Use the context. The Background function generates the Context of the root node
  • The Context is going to pass the necessary values, not everything, right
  • Context is multi-coroutine safe and can be used in multiple coroutines