SoFunction
Updated on 2025-03-05

Learn more about why Golang needs timeout control

1. Introduction

This article will introduce why timeout control is needed, and then introduce in detail the methods to implement timeout control in Go. Among them, we will discusstimePackage andcontextThe specific way of implementing timeout control for the package is to explain the applicable scenarios of both so that timeout control can be implemented in a more appropriate way in the program and improve the stability and reliability of the program.

2. Why timeout control is needed

Timeout control refers to the fact that when performing network requests or coroutine execution, in order to avoid the program waiting and causing resource waste, we need to set a timeout time for these operations. If the operation is not completed within the specified time, we need to stop waiting or terminate the operation.

For example, when making a network request, if a problem occurs on the server side, resulting in no timely response, the client may wait for the server to respond, which will cause waste of client resources.

To give a simple example, for example, we need to obtain a resource from a remote server, we can use the following code to implement it:

func getResource() (Resource, error) {
    conn, err := ("tcp", ":8888")
    if err != nil {
        return nil, err
    }
    defer ()
    // Send a request and wait for a response    _, err = ([]byte("GET /resource HTTP/1.1\r\nHost: \r\n\r\n"))
    if err != nil {
        return nil, err
    }
    resp, err := (conn)
    if err != nil {
        return nil, err
    }
    // parse the response and return the resource    return parseResource(resp)
}

But if the remote server does not respond after we send the request, then our program will wait and cannot continue to perform other tasks.

In some cases, this may cause blockage of the program, which affects the performance and stability of the program. Therefore, when performing network communication and other operations, especially when calling external APIs or accessing remote servers, timeout control must be used. Then, inGoIn the language, what are the implementation methods of timeout control?

3. Methods of timeout control

3.1 Time package implements timeout control

timePackages provide multiple ways to implement timeout control, includingfunction,Functions andFunctions, using them can implement timeout control, belowAs an example, how to use ittimePackage implements timeout control. The code example is as follows:

// Create a timertimer := (5 * )
defer ()

// Use a channel to listen for whether the task has been completedch := make(chan string, 1)     
go func() {         
// Simulate the task and sleep for 5 seconds    (2* )         
    ch <- "hello world"     
}()

// Wait for the result through the select statement, the task returns normallyselect {
case <-ch:
    ("Task completed normally")
  // ch has received the value, and the normal processing logic is usedcase <-:
    ("Timed out")
  // Timeout, go timeout logic}

In this example, we useMethod creates a timer with a timeout of 2 seconds. Then inselectUsed in a statement to wait for the result, and use which one returns first.

If the operation is completed within 2 seconds, the task is completed normally; if the operation is not completed after more than 2 seconds, thenselectIn the statement<-The value will be received and the timeout processing logic will be passed.

3.2 context implements timeout control

ContextInterfaces are a context management mechanism provided in the Go language standard library. It allows the transfer of context information between different parts of the program, and can use it to implement functions such as timeout control, cancel operations, and truncation operations. in,ContextAn interface existstimerCtxThe implementation of the , which can set a timeout time, after the timeout time is reached,timerCtxThe done channel of the object will be closed.

When it is necessary to determine whether the timeout is timed out, just callcontextThe object'sDonemethod, it will returntimerCtxThe done channel in the object. If there is data returned, it means that the timeout has been timed out. Based on this, we can implement timeout control. The code example is as follows:

// Create a timerCtx and set the timeout to 3 secondsctx, cancel := ((), 3*)     
// Call the cancel function to free the occupied resourcesdefer cancel()
// Use a channel to listen for whether the task has been completedch := make(chan string, 1)     
go func() {         
// Simulate the task and sleep for 5 seconds    (2* )         
    ch &lt;- "hello world"     
}()
// Wait for the result through the select statement, the task returns normallyselect {
    case &lt;-():
        ("timeout")
    case result := &lt;-ch:
        (result)
}

Passed hereCreate atimerCtx, set the timeout time, the timeout time is 3s. Then start a coroutine to execute the specific business logic.

AfterwardsselectStatement, righttimerCtxListen at the same time as the business execution result. When the task processing timeouts, the timeout logic is executed; if the task completes before the timeout, the normal processing flow is executed. In this way, the timeout processing of the request is realized.

4. Applicable scenario analysis

As can be seen from the above,timeandtimerCtxBoth can be used to implement timeout control, but in fact, the applicable scenarios of the two are actually different. In some scenarios, timeout control is not suitable for usetimeto implement, but to usetimerCtxIt is more appropriate to achieve it. In some scenarios, both implementation methods are possible.

Below I will briefly introduce several common scenarios and then analyze them so that they can be implemented appropriately in the appropriate scenario.

4.1 Simple timeout control

For example, suppose we need to obtain some data from a remote service, we can use the http package in the Go standard library to make network requests. The general request function is as follows:

func makeRequest(url string) (string, error) {
   // Request data}

In order to avoid the long response time of the request, which causes the program to be in a waiting state for a long time, we need to implement timeout processing for this function to ensure that the program can respond to other requests in a timely manner, rather than waiting all the time.

To achieve this, you can use ittimePack ortimerCtxto implement timeout control. existmakeRequestTimeout control is implemented in the function, the code here shows theMethods for timeout control of third pointThe code examples in it are roughly the same, just need to put the coroutinesleepJust switch the function to specific business logic, and I won't go into details here. Moreover, looking at the above code example, we can also seetimerortimerCtxIn this scenario, the difference is not big, and it can be replaced by each other at this time.

Therefore, for such scenarios that control the execution time of a certain function, you can choose anytimeortimerCtxOne of them is achieved.

4.2 Optional timeout control

Here we implement a method to establish a network connection. When the user calls this method, he passes in the list of addresses to be established. Then the method traverses the incoming address list and attempts to connect to each address until the connection is successful or all addresses are attempted to complete. The function definition is as follows:

func dialSerial(ras addrList) (Conn, error){
   // Execute the logic to establish a network connection}

Based on this, an optional timeout control function is implemented based on this function. If the user calls this method and has a specified timeout time, the timeout control will be performed at this time; if the timeout time is not specified, the timeout control will not be performed at this time. Used here separatelytimePackage andcontextaccomplish.

First of all,timeThe package implements optional timeout control, and can implement optional timeout control by passing the timer through the function parameter. Specifically, the timer may be used as aParameters of type are passed to the function and then used in the functionselectmonitorIt is timeout; if no timer instance is passed, the timeout control will not be performed by default. The code implementation is as follows:

func dialSerial(timeout , ras addrList) (Conn, error){
   // Execute the logic of establishing a network connection. When trying to establish a connection for each address, first check whether the timeout is reached.   for i, ra := range ras {
          // Use this to perform timeout control, first determine whether the timer instance is passed to          if timeout != nil {
              select {
              // Whether the listening timeout              case &lt;-:
                  return nil, ("timeout")
              default:
              }
          }
         // Execute the logic to establish a network connection later   }
}

Then usetimerCtxTo implement the implementation of timeout control, you can pass a functionInterface parameters to implement timeout control.

Specifically, the user can pass aThe interface implementation, if there is a specified timeout time, pass in onetimerCtxImplementation; if timeout control is not required, it can be transmitted in, it will never time out. Then the function is calledDoneMethod to determine whether the timeout is exceeded, thereby realizing timeout control. The code implementation is as follows:

func dialSerial(ctx , ras addrList) (Conn, error){
   // Execute the logic of establishing a network connection. When trying to establish a connection for each address, first check whether the timeout is reached.   for i, ra := range ras {
       select {
       case &lt;-():
          return nil, &amp;OpError{Op: "dial", Net: , Source: , Addr: ra, Err: mapErr(())}
       default: 
       }
       // Execute the logic to establish a network connection   }
}

View the above code,dialSerialThe function implements optional timeout control, it seems that the incoming parameters are different, one is the incoming timerExample, one is passed inInterface instances, but in fact it's not just that.

First of all, the readability of the code is passed inInstance to implement timeout control, notGoThe common implementation methods in the user are difficult to understand; and forInterfaces are widely used. If you want to implement timeout control, the user only needs to pass in one.timerCtxThe example is just for users, and the code is more readable.

Secondly, for the wholeGoIn terms of language ecology,Interfaces are widely used in the Go language standard library, and are generally timeout controls are used.timerCtxTo achieve, if aThe example is actually with the wholeGoThe language timeout control is out of place. AbovedialSerialAs an example, the method needs to establish a network connection to call the underlying function to assist in the implementation, such as:

func (fd *netFD) connect(ctx , la, ra ) (rsa , ret error) {
    // Execute the logic to establish the connection    switch err := connectFunc(, ra); err {
    // No error is reported, check whether the timeout is timed out at this time    case nil, :
       select {
       case &lt;-():
           // If the timeout has been completed, the timeout error will be returned at this time          return nil, mapErr(())
       default:
       }
     }
}

And just so happens that this function also implements optional timeout control, and it istimerCtxTo achieve, if it is passed in at this timetimerCtxThe timeout has been reached, and the function will directly return a timeout error.

If abovedialSerialThe timeout control is throughIf you implement the interface instance, when calling the function, the externalContextThe instance is passed as a parameterconnectFor functions, outer calls do not need to check whether the function timed out, and the code is more reusable.

Relatively, ifdialSerialThe timeout control is implemented through incoming timers, and it cannot be used well at this time.connectThe method has implemented the mechanism for timeout checking.

Therefore, in summary, useThe interface is an optional timeout control parameter, compared to using, more suitable and more efficient, with the wholeGoLanguage implementation can also be better integrated.

Summarize

ContextandTimeThey are all methods to implement timeout control in Go language. They each have their own advantages and disadvantages. It cannot be said which implementation is better. It is necessary to choose which method to use according to the specific scenario.

In some simple scenarios,TimePackage implementation timeout control may be more convenient because its API is simpler and only requires use()Functions can implement timeout control.

However, if multiple functions are involved, or multiple functions are requiredgoroutineIf you pass between them, use it at this timeContextTo implement timeout control may be more suitable.

5. Summary

This article introduces the reasons why timeout control is needed, mainly to avoid indefinite waiting, prevent resource leakage and improve program response speed.

Then we introduceGoMethods to implement timeout control in the language, including usingtimeImplement timeout control and usecontextImplement timeout control and give a simple code example.

Next, we analyze the applicable scenarios of these two implementations and clarify which scenarios are suitable for usetimeImplement timeout control and in which scenarios to usetimerCtxto achieve more efficient.

Based on this, the introduction to why timeout control is needed has been completed. I hope that everyone can better implement it in scenarios where timeout control is needed.

The above is a detailed content to understand why Golang needs timeout control. For more information about Golang timeout control, please follow my other related articles!