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
emptyCtx
Is a variable of type int, but implements the interface to the context.emptyCtx
There’s no timeout, you can’t cancel, you can’t store any extra information, soemptyCtx
Used as the root node of the context tree.- But we don’t usually use them directly
emptyCtx
, but use byemptyCtx
The two variables instantiated (background, todo) are called, respectivelyBackground
andTODO
Method, but the implementation of these two contexts is the same.
The difference between Background and TODO:
Background
and
TODO
Just for different scenarios:
Background
Usually used in main functions, initializations, and tests, as a top-level
context
That is to say, in general we create
context
Are based on
Background
; while
TODO
Are you unsure of what to use
context
When you use it.
The spanning tree function
- You can use the context. Background() gets a root Context.
- Once you have the root node, use the following four functions to generate the Context tree:
- WithCancel(parent Context) : Generates a cancellable Context.
- WithDeadline(parent Context, d time. time) : Generates a timeable cancelable Context with d as the specific time of the cancellation.
- WithTimeout(parent Context, timeout time.duration) : Generates a timeout cancelable Context with the timeout parameter setting how long to cancel
- 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