Consider this (https://play.golang.org/p/zvDiwul9QR0):
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
for {
select {
case <-ctx.Done():
fmt.Println("Done")
break
default:
for {
fmt.Println("loop")
time.Sleep(500 * time.Millisecond)
}
}
}
}
So here the contexts returns a "Done()" channel after 2 seconds. And I want to catch this and cancel my infinite for loop. The code example above does not do this, it never exits the loop.
How can I achieve this?
Context cancelation is not magic - it's just a signal mechanism. To abort work, you still need to monitor the state of the context
from within your worker goroutine:
func myTask(ctx context.Context, poll time.Duration) error {
ticker := time.NewTicker(poll)
defer ticker.Stop()
for {
fmt.Println("loop")
select {
case <-ticker.C:
case <-ctx.Done():
return ctx.Err()
}
}
}
https://go.dev/play/p/s6dSr9qqKJU
also as Eli pointed out, break
will only break out of the select statement - so you need something more precise to break out of a loop. Refactoring into functions make return
's much more intuitive for task abortion.
Following up from comments. I would refactor your task like so:
// any potentially blocking task should take a context
// style: context should be the first passed in parameter
func myTask(ctx context.Context, poll time.Duration) error {
ticker := time.NewTicker(poll)
defer ticker.Stop()
for {
fmt.Println("loop")
select {
case <-ticker.C:
case <-ctx.Done():
return ctx.Err()
}
}
}