Immediately Cancel Goroutine?

252 views
Skip to first unread message

Frank

unread,
Apr 12, 2023, 9:29:14 AM4/12/23
to golang-nuts
Hi Gophers,

So I've run into a situation where my code would run for a rather long time (10minutes - ~1 hour). And I want to cancel it halfway sometimes. I know I can use channel/context can check for channel message to return early. But this requires explicitly checking for channel status and would introduce many messy code blocks. Is it feasible (and good) for me to listen to some signal and use panic to stop the execution once signal received? 

Thanks!
Frank 

Jesper Louis Andersen

unread,
Apr 12, 2023, 9:41:50 AM4/12/23
to Frank, golang-nuts
It depends.

If you know everything you have done is safely on disk somewhere, or you have a stateless system, you can end the application by a panic.

However, in the event your program or requirements change, it can be desirable to mix in lifecycle management early on. This includes graceful shutdowns. Imagine, for instance, a system which loads a database into memory and serves requests for clients. It's completely stateless, but you might want to linger until all client requests are done before doing a shutdown. This requires life-cycle management. In general, as software grows, you can expect it to require more graceful handling of its environment, so it might not be necessary right now, but it can be in the future.

Periodically checking context.Done() is good form, because it makes your go-routines cooperate with the rest of the program. The only thing that can clean up is your goroutine, and that cleanup usually happens once the context.Done() signal is given. As a rule of thumb: if there's a user involved somewhere in the loop, you should think long and hard about a quick panic before doing so.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/d8bb51be-4f21-4658-93a9-d0e197b9bdden%40googlegroups.com.


--
J.

burak serdar

unread,
Apr 12, 2023, 10:45:03 AM4/12/23
to Frank, golang-nuts
On Wed, Apr 12, 2023 at 7:29 AM Frank <francis...@gmail.com> wrote:
Hi Gophers,

So I've run into a situation where my code would run for a rather long time (10minutes - ~1 hour). And I want to cancel it halfway sometimes. I know I can use channel/context can check for channel message to return early. But this requires explicitly checking for channel status and would introduce many messy code blocks. Is it feasible (and good) for me to listen to some signal and use panic to stop the execution once signal received? 

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.

Ian Lance Taylor

unread,
Apr 12, 2023, 12:24:32 PM4/12/23
to burak serdar, Frank, golang-nuts
On Wed, Apr 12, 2023 at 7:44 AM burak serdar <bse...@computer.org> wrote:
>
> 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
> }

It turns out that you can make that shorter still:

canceled := func() bool { return ctx.Err() != nil }

Ian
Reply all
Reply to author
Forward
0 new messages