How to implement timeout in go routines

Go lang simple workers with timeout using context

How to implement timeout in go routines
Page content

I wanted to implement a timeout in go routines. There are a couple o ways doing that. We could use context.WithTimeout() or we go with classical sync.WaitGroup group with a trick.

Let’s start with the first one.

context timeout

Let’s start with creating a context with a timeout.

    ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
    defer cancel()

and a couple of channels:

    jobs := make(chan int, noOfJobs)
    results := make(chan int, noOfJobs)
    quitCh := make(chan bool, 1)

I assume to spin up a couple of workers, to consume jobs, and one collector to collect all results:

    for w := 1; w <= noOfWorkers; w++ {
        go worker(w, jobs, results)
    }
	
    go collector(ctx, results, quitCh)

and finally, let us send some things to do:

    for a := 1; a <= noOfJobs; a++ {
        jobs <- a
    }

    <-quitCh
	
    fmt.Printf("globalResult: %v\n", globalResult)

How the worker works, it’s pretty straightforward. We get jobs from the job channel and push out results to results channel.

func worker(id int, job <-chan int, results chan<- int) {
    for j := range job {
        fmt.Printf("worker %d started job %d\n", id, j)
        time.Sleep(time.Second)
        fmt.Printf("worker %d finished job %d\n", id, j)
        results <- j
	}
}

Collector is more sophisticated. We run in an infinite loop, collecting that from the channel. Two options to break it is either to collect all results (length of globalResults is equal to noOfJobs), or we get a timeout, and we are using quitCh as a way to send a close message.

func collector(ctx context.Context, results <-chan int, quitCh chan<- bool) {
	for {
		select {
		case j := <-results:
			fmt.Printf("result: %d\n", j)
			globalResult = append(globalResult, j)
			// check if we are done
			if len(globalResult) == noOfJobs {
				println("full")
				quitCh <- true
			}
		case <-ctx.Done():
			println("ctx done")
			quitCh <- true
			break
		}
	}
}

Complete code here

wait group

The second approach is not mine but works in simpler cases: