When I want ctx timeout, what should I do to completely terminate the method that is executing longRunningCalculation()?
package main
import (
"context"
"log"
"time"
)
func longRunningCalculation(timeCost int) chan string {
result := make(chan string)
go func() {
time.Sleep(time.Second * (time.Duration(timeCost)))
log.Println("Still doing other things...") //Even if it times out, this goroutine is still doing other tasks.
result <- "Done"
log.Println(timeCost)
}()
return result
}
func jobWithTimeout() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case <-ctx.Done():
log.Println(ctx.Err())
return
case result := <-longRunningCalculation(3):
log.Println(result)
}
}
func main() {
jobWithTimeout()
time.Sleep(time.Second * 5)
}
What did you expect to see? 2019/09/25 11:00:16 context deadline exceeded
What did you see instead? 2019/09/25 11:00:16 context deadline exceeded 2019/09/25 11:00:17 Still doing other things...
To stop the goroutine started by longRunningCalculation
when the caller's context times out, you need to pass ctx
into longRunningCalculation
and explicitly handle the context timing out, the same way you do in jobWithTimeout
Doing things that way also means instead of calling time.Sleep
, that time.Tick
will be a better choice, so both timers are running at the same time. Like so:
package main
import (
"context"
"log"
"time"
)
func longRunningCalculation(ctx context.Context, timeCost int) chan string {
result := make(chan string)
go func() {
calcDone := time.Tick(time.Second * time.Duration(timeCost))
log.Printf("entering select (longRunningCalculation)")
select {
case <-ctx.Done():
result <- "Caller timed out"
return
case <-calcDone:
log.Println("Still doing other things...") //Even if it times out, this goroutine is still doing other tasks.
result <- "Done"
}
log.Println(timeCost)
}()
return result
}
func jobWithTimeout() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
result := longRunningCalculation(ctx, 3)
log.Printf("entering select (jobWithTimeout)")
select {
case <-ctx.Done():
log.Println(ctx.Err())
return
case res := <-result:
log.Println(res)
}
}
func main() {
jobWithTimeout()
}