Control child coroutine exit
The context package provides a mechanism for communication and control between multiple goroutines. Using the Context package can effectively control the concurrency of the program and improve the robustness and performance of the program.
Golang has no way to get other goroutines to exit, goroutines can only exit by themselves. The reason why the context package can control the exit of the child coroutine means that the child coroutine can receive the exit signal sent by the main coroutine and then exit itself. See the following example code:
package main import ( "context" "errors" "sync" ) func request(ctx , url string) error { result := make(chan int) err := make(chan error) go func() { // If isSuccess is the result returned by the request, success information will be passed through result, error message will be passed through error isSuccess := true if isSuccess { result <- 1 } else { err <- ("some error happen") } }() select { case <-(): // Other requests failed return () case e := <-err: // This request failed, return an error message return e case <-result: // This request is successful and no error message is returned return nil } } func main() { ctx, cancel := (()) // Call interface a err := request(ctx, "/a") if err != nil { return } wg := {} // Call interface b (1) go func() { defer () err := request(ctx, "/b") if err != nil { cancel() } }() // Call interface c (1) go func() { defer () err := request(ctx, "/c") if err != nil { cancel() } }() () }
First, the method is called to construct a Context and returns a cancel function. The other methods called by goroutines pass in this Context as the first parameter. When the main goroutine wants to tell all goroutines that need to exit, the exit information is told to all goroutines by calling the cancel function. All goroutines get an exit signal and then exit by listening for the returned channel.
Timeout control
For example, in scenarios such as querying databases, calling RPC services, calling HTTP interfaces, etc., these operations are blocked. If data is not returned, it will affect the user experience of the product. The solution to these situations is usually to set a timeout, which will automatically cancel the operation after it exceeds.
This workaround can be achieved using the WithDeadline and WithTimeout methods in the context package. Let’s take a look at the following example:
package main import ( "context" "fmt" "time" ) func main() { ctx, cancel := ((), 50*) defer cancel() select { case <-(1 * ): ("overslept") case <-(): (()) // Output "context deadline exceeded" } }
Because the set timeout time is 50 milliseconds, select will enter the second case and output "context deadline exceeded".
Golang's net/http package implements timeout control when initiating http requests. See the following code:
package main import ( "context" "io" "log" "net/http" "time" ) func main() { req, err := (, "", nil) if err != nil { (err) } // Construct a Context with a timeout of 50 milliseconds ctx, cancel := ((), 50*) defer cancel() req = (ctx) c := &{} res, err := (req) if err != nil { (err) } defer () out, err := () if err != nil { (err) } (string(out)) }
After execution, "context deadline exceeded" will be output. If the timeout parameter of the method is increased, you can see the normal return data.
Context pass data
This function is very important in link tracking. Link tracking requires the traceID to be passed down layer by layer and between services.
type traceIdKey struct{}{} // Define a fixed keyvar TraceIdKey = traceIdKey{} func ServeHTTP(w , req *){ // First get the traceID from the request traceId := getTraceIdFromRequest(req) // Save Key into Context ctx := ((), TraceIdKey, traceId) // Set the timeout time ctx = (ctx, ) // Carry traceId to initiate rpc request repResp := RequestRPC(ctx, ...) // Carry traceId to query DB dbResp := RequestDB(ctx, ...) // ... } func RequestRPC(ctx , ...) interface{} { // Get the traceid and record the log when calling rpc traceId, _ := (TraceIdKey) // Make a request // ... return }
After receiving the request, the traceId is obtained through req and recorded in the Context. When calling other RPC services and querying DB, the constructed Context is passed in. In subsequent code, the stored traceId can be obtained through Context.
The above is the detailed explanation of the usage scenarios and examples of the context package in Golang. For more information about the usage of the context package, please pay attention to my other related articles!