SoFunction
Updated on 2025-03-05

Detailed explanation of usage scenarios and examples of context package in Golang

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!