Search code examples
goconcurrency

Cancel goroutine on new function call


I need to cancel any previous goroutine on every function call. How would that be handled in Go? I've seen channels used but I can't quite wrap my head around the examples and whether a select statement is necessary.

The desired result would be that only the last request subtasks are ran.

package main

import (
    "fmt"
    "time"
)

func main() {
    for i := 0; i < 5; i++ {
        go handleRequest(i)
        time.Sleep(1 * time.Second) // Time between requests
    }
}

func handleRequest(incr int) {
    fmt.Println("New request registered: ", incr + 1)
    for i := 0; i <= 3; i++ {
        fmt.Println("Request: ", incr + 1, " | Sub-task: ", i + 1)
        time.Sleep(2 * time.Second) // Time processing
    }
    return
}

Solution

  • Goroutine cancellation can be done using a context. If you need to cancel a previous goroutine, start it with a context and cancel it when a new one starts. You have to write the goroutine to periodically check for the context:

       var ctx context.Context
       var cancel context.CancelFunc
       for i := 0; i < 5; i++ {
            if cancel!=nil {
                 // cancel previous goroutine
                 cancel()
            }
            ctx,cancel=context.WithCancel(context.Background())
            // start goroutine with a new context
            go handleRequest(ctx,i)
            time.Sleep(1 * time.Second) // Time between requests
        }  
        if cancel!=nil {
           cancel() 
        }
    

    In your goroutine, you have to check for cancellation:

    func handleRequest(ctx context.Context,incr int) {
        fmt.Println("New request registered: ", incr + 1)
        for i := 0; i <= 3; i++ {
            fmt.Println("Request: ", incr + 1, " | Sub-task: ", i + 1)
            time.Sleep(2 * time.Second) // Time processing
            select {
               case <-ctx.Done():
                 // canceled
                 return
               default:
                 // not canceled
            }
        }
        return
    }