1

So I have this code, it accepts user entry of one, two, or three. It prints Ticker ticked depending on the duration selected. I need help on how to stop the ticker first before activating the ticker again with a different duration.

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
    "time"
)

func main() {
    reader := bufio.NewReader(os.Stdin)

    for {
        fmt.Print("> ")

        text, _ := reader.ReadString('\n')
        text = strings.Replace(text, "\n", "", -1)

        switch text {
        case "one":
            go timeTick(true, 1)
        case "two":
            go timeTick(true, 2)
        case "three":
            go timeTick(true, 3)
        default:
            go timeTick(false, 0)
        }
    }
}

func timeTick(flag bool, tick int) {
    var tickChan *time.Ticker

    if flag {
        tickChan = time.NewTicker(time.Second * time.Duration(tick))
    }

    doneChan := make(chan bool)

    if !flag {
        doneChan <- true
    }

    for {
        select {
        case <-tickChan.C:
            fmt.Println("Ticker ticked")
        case <-doneChan:
            fmt.Println("Done")
            return
        }
    }
}

So user inputs either, one, two, or three to activate the ticker else it will send true to doneChan channel.

When I activated the ticker with one. It prints Ticker ticked every second but how do I stop the ticker when I input two or three while the ticker is running? It then brings me to my main question, why is doneChan not triggered at all even inputting a random string?

Jahm
  • 658
  • 1
  • 12
  • 27
  • 1
    You cannot send and receive on channel `doneChan` in the same goroutine. I suggest sending the done signal from the main goroutine. – dev.bmax Jan 17 '18 at 22:42
  • Are you expecting separate calls to `timeTick` to somehow execute `doneChan <- true` from another goroutine? – JimB Jan 17 '18 at 22:44
  • @JimB yes that's the idea. – Jahm Jan 17 '18 at 23:39
  • @dev.bmax what are needed to be changed on my code? Can you please show me how? – Jahm Jan 17 '18 at 23:43
  • @Jahm: you have some broad misunderstandings of how things work, but in short you're going to want to pass in the control channels into your functions, either as arguments or in closures. – JimB Jan 17 '18 at 23:47
  • Check out this example: https://stackoverflow.com/a/36709984/4283005 – dev.bmax Jan 18 '18 at 00:06

2 Answers2

3

You need to pass in the channel used to signal completion. You always want to cancel the current ticker if there is one, so you can attempt a send in each iteration of the main for loop. Since the channel isn't buffered, a send operation is blocking, so you need to attempt the send in a select statement with a default case to prevent a deadlock in the case you don't have a current ticker attempting to read from the channel.

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
    "time"
)

func main() {
    reader := bufio.NewReader(os.Stdin)
    tickerDone := make(chan struct{})

    for {
        fmt.Print("> ")

        text, _ := reader.ReadString('\n')
        text = strings.Replace(text, "\n", "", -1)

        select {
        case tickerDone <- struct{}{}:
        default:
        }

        switch text {
        case "one":
            go timeTick(1, tickerDone)
        case "two":
            go timeTick(2, tickerDone)
        case "three":
            go timeTick(3, tickerDone)
        }
    }
}

func timeTick(tick int, done <-chan struct{}) {
    tickChan := time.NewTicker(time.Second * time.Duration(tick))

    for {
        select {
        case <-tickChan.C:
            fmt.Println("Ticker ticked")
        case <-done:
            fmt.Println("Done")
            return
        }
    }
}
Gavin
  • 4,365
  • 1
  • 18
  • 27
0

You can try this code below also. I put buffered channel outside the function for synchronize execution, I also used channel receiver to assign and to stop channel.

func timeTick(flag bool, tick int,doneChan chan bool) {
    var tickChan *time.Ticker
    msg := make(<-chan time.Time) // channel receiver

    if flag {
        tickChan = time.NewTicker(time.Second * time.Duration(tick))
    }

    if !flag {
        doneChan <- true    
        <-msg
    }

    if tickChan != nil { // Check if nil
        msg = tickChan.C
    }

    for {
        select {
            case <-msg:
                fmt.Println("Ticker ticked ")
            case <-doneChan:
                fmt.Println("Done ")
                return
        }
    }
}

func main() {

    reader := bufio.NewReader(os.Stdin)
    doneChan := make(chan bool,1)
    for 
    {
        fmt.Print("> ")
        text, _ := reader.ReadString('\n')
        text = strings.Replace(text, "\n", "", -1)

        switch text {
        case "one":
            go timeTick(true, 1, doneChan)
        case "two":
            go timeTick(true, 2, doneChan)
        case "three":
            go timeTick(true, 3, doneChan)
        default:
            go timeTick(false, 0, doneChan)
        }
    }

}
Adriel Artiza
  • 327
  • 4
  • 12