3

I have a select block that is listening on 2 channels, a ticker and a timer:

package main

import (
    "fmt"
    "time"
)

func main() {

    ticker := time.NewTicker(5 * time.Second)
    for {
        select {
        case z := <-ticker.C:
            fmt.Printf("tick %d\n", z)

        case <-time.After(12 * time.Second):
            fmt.Println("12 seconds elapsed!")
        }
    }
}

If I run the code, the time.After case never runs, but the ticker works correctly.

If I remove the ticker, time.After fires correctly:

package main

import (
    "fmt"
    "time"
)

func main() {

    for {
        select {
        case <-time.After(12 * time.Second):
            fmt.Println("12 seconds elapsed!")
        }
    }
}

It works correctly if I used a timer rather than time.After:

package main

import (
    "fmt"
    "time"
)

func main() {

    ticker := time.NewTicker(5 * time.Second)
    timer := time.NewTimer(12 * time.Second)
    for {
        select {
        case z := <-ticker.C:
            fmt.Printf("tick %d\n", z)

        case <-timer.C:
            fmt.Println("12 seconds elapsed!")
        }
    }
}

Why does this happen?

F21
  • 32,163
  • 26
  • 99
  • 170
  • I don't understand the question. 12sec is always longer than 5sec; can you explain your rationale of how the time.After _would_ fire in less than 5sec? – JimB Jun 08 '16 at 02:26
  • I am not expecting it to fire in less than 5 seconds. I am expecting a tick at 5 seconds, another tick at 10 seconds and at 12 seconds, `time.After` should fire. However, unless I use `time.NewTimer` to create the timer, `time.After` never fires no matter how long I wait. – F21 Jun 08 '16 at 02:36

2 Answers2

10

A select blocks until one of its cases is ready, then it executes that case. In your example time.After() never gets called.

func main() {

    ticker := time.NewTicker(5 * time.Second)
    for {
        select {
        case z := <-ticker.C:
            fmt.Printf("tick %d\n", z)

        //This never gets chance to be ready. It'll work if you make it less than 5 seconds.
        case <-time.After(12 * time.Second): 
            fmt.Println("12 seconds elapsed!")
        }
    }
}

You can get it working by having timer declared before for loop.

func main() {

        ticker := time.NewTicker(5 * time.Second)
        timer := time.After(12 * time.Second)
        for {
                select {
                case z := <-ticker.C:
                        fmt.Printf("tick %d\n", z)

                case <-timer:
                        fmt.Println("12 seconds elapsed!")
                }   
        }   
}   
ppp
  • 975
  • 7
  • 15
2

The key is, when select is called, it will re-create the channel in all case clause. If you create a timer in case <- newCreateTimerChannel, it will start a new timer. So put the timer creation outside the for loop to make it as a global timer.

whiledoing
  • 51
  • 6