Any recipe to stop a goroutine of a function other than of a channel?

627 views
Skip to first unread message

Zhaoxun Yan

unread,
Apr 16, 2022, 9:30:44 AM4/16/22
to golang-nuts
Timeout  is quite common practice in programming. For example, Listen function in internet connection with a timeout may just close the connection and Listen thread as silence persists for some period, like 60 seconds.

I found it very hard to implement such a general shutdown feature of a thread/goroutine on such I/O functions, here is one unsuccessful try, which I want to stop the whole in 9 seconds but it does not function as I wanted:

package main

import (
    "fmt"
    "time"
    )

func wait(){
    time.Sleep(5 * time.Second)
    fmt.Println("wait 1st signal")
    time.Sleep(5 * time.Second)
    fmt.Println("wait 2nd signal")
}


func main() {
   ticker := time.NewTicker(3 * time.Second)
   i := 0
   for{
       select{
        case <- ticker.C:
            i++
            fmt.Printf("ticker rings %d\n", i)
            if i==3{
               return
            }
      default:
          wait()
      }      
   }

}

The result is to wait whole 30 seconds:
wait 1st signal
wait 2nd signal
ticker rings 1
wait 1st signal
wait 2nd signal
ticker rings 2
wait 1st signal
wait 2nd signal
ticker rings 3

An online course suggests to wrap up the wait/Listen function with a channel (which would return something instead of nothing above)

go func(){
  resultChan <- Listen()
}()

select{
case a := <- resultChan:
   //analyze a
case <-ticker.C:
   //might break or return
}

But this recipe obviously only kill the select goroutine rather than the anonymous goroutine that actually runs Listen function. My question is - how to kill the waiting or listening goroutine from outside?

Robert Engels

unread,
Apr 16, 2022, 9:46:59 AM4/16/22
to Zhaoxun Yan, golang-nuts
2 options: put a timeout on the listen and loop. Close the socket it is listening on. 

On Apr 16, 2022, at 8:30 AM, Zhaoxun Yan <yan.z...@gmail.com> wrote:


--
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/e774c5a4-ed53-43da-a1fe-0d617603e223n%40googlegroups.com.

roger peppe

unread,
Apr 16, 2022, 10:15:05 AM4/16/22
to Zhaoxun Yan, golang-nuts
Most network functions provide a way to provide a timeout or cancellation explicitly. Listen is one such: see this method - if the context that it's passed is cancelled, the Listen call will return. https://pkg.go.dev/net#ListenConfig.Listen

The most general way to stop waiting on timeout or cancellation in the absence of explicitly provided functionality is to start the function in a separate goroutine and send notification when the function completes. This is non-ideal because the goroutine will remain around even when the waiter has given up, but can still be a useful technique in some circumstances.

Hope this helps,
  rog.

--

Zhaoxun Yan

unread,
Apr 16, 2022, 10:25:29 AM4/16/22
to roger peppe, golang-nuts
Thanks rog!

  I already dealt with it in my project since the Listen of gorilla websocket did as you just mentioned - close and even throw out an error. But I am surprised at why golang still has not provided a general feature on that.

Zhaoxun

roger peppe

unread,
Apr 16, 2022, 11:05:11 AM4/16/22
to Zhaoxun Yan, golang-nuts
I'm not sure what you think such a general feature might look like.

In general, it's not a good idea to force an arbitrary thing to stop, because that can leave resources in an undefined state, so the only reasonable approach AFAICS is to ask the function that's running to stop. That functionality is provided by the language with channels and by the standard library with the context package.

Brian Candler

unread,
Apr 16, 2022, 4:39:49 PM4/16/22
to golang-nuts
For TCP sockets, there's SetDeadline, SetReadDeadline and SetWriteDeadline.  See https://pkg.go.dev/net

For the originally posted code, I would replace

    time.Sleep(5 * time.Second)

with code which either waits 5 seconds, or terminates on a shutdown signal if that is received first.  The idiomatic way to do that is with a context.

select {
  case <-ctx.Done():
    return   // context cancelled
  case <-time.After(5 * time.Second):
Reply all
Reply to author
Forward
0 new messages