Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't this Golang code to select among multiple time.After channels work?

Why doesn't this Golang code to select among multiple time.After channels work?

See code below. The 'timeout' message is never issued. Why?

package main

import (
    "fmt"
    "time"
)

func main() {
    count := 0
    for {
        select {
        case <-time.After(1 * time.Second):
            count++
            fmt.Printf("tick %d\n", count)
            if count >= 5 {
                fmt.Printf("ugh\n")
                return
            }
        case <-time.After(3 * time.Second):
            fmt.Printf("timeout\n")
            return
        }
    }
}

Run it on Playground: http://play.golang.org/p/1gku-CWVAh

Output:

tick 1
tick 2
tick 3
tick 4
tick 5
ugh
like image 762
Everton Avatar asked Jan 27 '16 11:01

Everton


People also ask

What does blocking mean Golang?

Blocking Statements This means: A statement to receive data from a channel will block until some data is received. A statement to send data to a channel will wait until the sent data has been received.

How channel works in Golang?

In Go language, a channel is created using chan keyword and it can only transfer data of the same type, different types of data are not allowed to transport from the same channel. You can also create a channel using make() function using a shorthand declaration.

How does Golang select work?

The select statement lets a goroutine wait on multiple communication operations. A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.


2 Answers

Because time.After is a function, so on every iteration it returns a new channel. If you want this channel to be the same for all iterations, you should save it before the loop:

timeout := time.After(3 * time.Second)
for {
    select {
    //...
    case <-timeout:
        fmt.Printf("timeout\n")
        return
    }
}

Playground: http://play.golang.org/p/muWLgTxpNf.

like image 94
Ainar-G Avatar answered Dec 30 '22 09:12

Ainar-G


Even @Ainar-G has already provided the answer, another possibility is to use time.Tick(1e9) to generate a time tick on every second and then listen for timeAfterchannel after the specified period.

package main

import (
    "fmt"
    "time"
)

func main() {
    count := 0
    timeTick := time.Tick(1 * time.Second)
    timeAfter := time.After(5 * time.Second)

    for {
        select {
        case <-timeTick:
            count++
            fmt.Printf("tick %d\n", count)
            if count >= 5 {
                fmt.Printf("ugh\n")
                return
            }
        case <-timeAfter:
            fmt.Printf("timeout\n")
            return
        }
    }
}
like image 31
Endre Simo Avatar answered Dec 30 '22 10:12

Endre Simo