Advanced Golang

Concurrency

Concurrency practices Time out code

In this tutorial, we explore a popular concurrency practice in Golang: implementing timeouts using the time.After function. Many interactive applications need to respond within a specific time frame, and Golang's concurrency model provides a neat approach to control how long a request or process runs.

The time.After function waits for a specified duration and then sends the current time on its returned channel. Its signature is as follows:

func After(d Duration) <-chan Time

This function is extremely useful when incorporated into a select statement to implement blocking timeouts.

Using time.After in a Select Statement

The following steps will guide you through creating a channel, launching a goroutine to send a value, and then using a select statement to either receive the value or trigger a timeout if the operation takes too long.

Step 1: Creating a Channel and Launching a Goroutine

First, you create a channel and start a goroutine called sendValue, which sends a value over the channel:

package main

func main() {
    ch1 := make(chan int)
    go sendValue(ch1)
}

Step 2: Defining the sendValue Function

Next, implement the sendValue function that sends a value (for instance, 10) into the channel:

package main

func main() {
    ch1 := make(chan int)
    go sendValue(ch1)
}

func sendValue(ch1 chan int) {
    ch1 <- 10
}

Step 3: Incorporating a Timeout Case Using time.After

Modify the main function to utilize a select statement. In this statement, the first case listens for a message from the channel, while the second case uses time.After to enforce a timeout. This approach is ideal for scenarios where a RESTful API call, for example, should not wait more than one second:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan int)
    go sendValue(ch1)

    select {
    case msg := <-ch1:
        fmt.Println(msg)
    case <-time.After(1 * time.Second):
        fmt.Println("select timeout")
    }
}

func sendValue(ch1 chan int) {
    ch1 <- 10
}

When you run this program, the output will display 10 because the channel operation completes immediately.

Step 4: Simulating a Delayed Response

To observe the timeout behavior, adjust the sendValue function to delay sending the value for more than one second (for instance, three seconds). This adjustment ensures that the select statement triggers the timeout case:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan int)
    go sendValue(ch1)

    select {
    case msg := <-ch1:
        fmt.Println(msg)
    case <-time.After(1 * time.Second):
        fmt.Println("select timeout")
    }
}

func sendValue(ch1 chan int) {
    time.Sleep(3 * time.Second)
    ch1 <- 10
}

To run the program, execute the following command in your terminal:

go run main.go

The resulting output will be:

select timeout

In this scenario, the timeout case is executed after one second because the goroutine’s delay surpasses the specified duration.

Note

The time.After function is an elegant tool for implementing timeouts in concurrent operations. It is especially useful in non-blocking scenarios where you want to avoid indefinite waiting during lengthy API or IO calls.

Summary

The time.After function is a powerful mechanism for handling timeouts in Golang. By integrating it within a select statement, you can ensure that your code does not stall during prolonged API or IO operations, thus making your applications more responsive and fault-tolerant.

Happy coding, and see you in the next lesson!

Watch Video

Watch video content

Practice Lab

Practice lab

Previous
Concurrency Practices Buffered and Unbuffered Channels