0

So, I have this (just an example):

package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(5 * time.Second)
    for {
        select {
        case <-ticker.C:
            fmt.Println("hello")
        }
    }
}

This is an infinite loop, and I want it that way. In the real code it loops every 1 hour. But, what if I want to call a func to make it stop looping? Is this possible? Something like:

func stop() {
//this will stop the main function from looping
}

I know I could do something like:

package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(5 * time.Second)
    done := make(chan bool)
    go func() {
        for {
            select {
            case <-done:
                fmt.Println("done")
                ticker.Stop()
                return
            case <-ticker.C:
                fmt.Println("hello")
            }
        }
    }()

    time.Sleep(10 * time.Second)
    done <- true
}

But that would be stoping the function from a predefined time frame(in this case 10 seconds), which is not what I want and also this is all within the same function, I need to make a call from outside the main function.

Is this possible somehow?

Pedro
  • 11
  • 3
  • 1
    Does this answer your question? [How to stop a goroutine](https://stackoverflow.com/questions/6807590/how-to-stop-a-goroutine) – ttrasn Jul 16 '20 at 05:09
  • 2
    You could use `case _, ok := <-ticker.C:` to adjust whether the channel is closed. To stop the ticker, you could just call `ticker.Stop()`. – hyz Jul 16 '20 at 05:25
  • @ttrasn sadly, no – Pedro Jul 16 '20 at 05:43
  • @hyz but how can I call ticker.Stop() from another function? Same question for closing the channel, can I close it in another function? Thanks for the replies – Pedro Jul 16 '20 at 05:44
  • @Pedro, of course, you could pass ticker to another function and call `ticker.Stop()` there. Similarly, you could `close(channel)` in another function. – hyz Jul 16 '20 at 06:53

1 Answers1

1

Here:

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func stop(ch chan<- struct{}) {
    select {
    // Triggers after 7 seconds
    case <-time.After(7 * time.Second):
        ch <- struct{}{}
    }
}

func signalStop(ch chan<- struct{}) {
    // Use a buffered channel (size = 1)
    sig := make(chan os.Signal, 1)
    // Use SIGINT signal i.e.,  <kill -SIGINT XXXX> or <Control+C>
    signal.Notify(sig, syscall.SIGINT)
    select {
    // Capture the SIGINT signal
    case <-sig:
        // Close the channel
        close(ch)
    }
}

func main() {
    ticker := time.NewTicker(1 * time.Second)
    done := make(chan struct{})
    // Spawn a goroutine with the done channel
    go signalStop(done)
    for {
        select {
        case <-ticker.C:
            fmt.Println("Hello!")
        case <-done:
            // When stop signals, stop the ticker and return
            ticker.Stop()
            fmt.Println("Bye, bye!")
            return
        }
    }
}

I've commented the relevant parts of the code so that you understand what I'm trying to do.

shmsr
  • 3,802
  • 2
  • 18
  • 29
  • 1
    You can close the channel in stop as well rather than sending an empty struct to the channel. – shmsr Jul 16 '20 at 07:03
  • in this code the for loops are extraneous. One solution involved a global ticker reachable from any function scope of the main package. –  Jul 16 '20 at 07:04
  • @mh-cbon Yes, agreed. – shmsr Jul 16 '20 at 07:05
  • 1
    @shmsr thanks for the reply! Looking good, but, just a question: You used time.After to return an empty struct to the channel. Is there a way I can send that struct at any point in time? For example: In your code you spawn the stop function right when you start the go routine. But, I dont know how much I want to wait to stop it... Sometimes Ill have it run for 3 hours, sometimes for 10 hours. What I need to do is call the stop function "on the fly" (using a console command for example) and that will stop the goroutine from running. Sorry if I didnt understand correctly your example. – Pedro Jul 16 '20 at 07:26
  • Yeah, you can definitely play with it. If you want to stick with time, then pass the values it through the function arguments as per your desire. In case, you want to send a signal to stop that goroutine, that's also possible. Let me update the solution, then. – shmsr Jul 16 '20 at 07:44
  • 1
    @shmsr yup, thats what Id like! A way to send a signal to stop that goroutine, precisely this! – Pedro Jul 16 '20 at 08:10