There are ways to reduce the boilerplate code. For instance:
func LongTask(ctx context.Context) {
canceled:=func() bool {
select {
case <-ctx.Done(): return true
default:
}
return false
}
...
if canceled() {
return
}
}
Or, if this involves lots of nesting, you can define:
canceled:=func() {
select {
case <-ctx.Done(): panic(errCanceled)
default:
}
}
so every now and then you can simply call canceled(). If you do this though, you need to recover:
go func() {
defer func() {
if r:=recover(); r!=nil {
if errors.Is(r,errCanceled) {
// canceled
} else {
panic(r)
}
}
}()
LongTask(ctx)
}()
There is no way you can immediately stop a goroutine. Handling of a signal has to happen in a separate goroutine, and you would still need to check periodically in the actual goroutine you want to cancel.