Milo Wielondek
Milo Wielondek

Reputation: 4352

Select within goroutine evaluates every other statement

Playing around with Go's channels and routines I have come across a peculiar behaviour I was hoping somebody could explain.

Below is a short program that is supposed to print a couple strings to stdout, by sending the strings through a channel to a "listener" (the select statement) running in a separate goroutine.

package main

import (
    "fmt"
    "time"
)

func main() {
    a := make(chan string)

    go func() {
        for {
            select {
            case <-a:
                fmt.Print(<-a)
            }
        }
    }()

    a <- "Hello1\n"
    a <- "Hello2\n"
    a <- "Hello3\n"
    a <- "Hello4\n"        
    time.Sleep(time.Second) 
}

Using

go func() {
    for s := range a {
        fmt.Print(s)
    }
}()

// or even simpler

go func() {
    for {
        fmt.Print(<-a)
    }
}()

works as expected. However, running the uppermost snippet with the select statement produces the following output:

Hello2
Hello4

i.e. only every other statement is printed. What kind of sorcery is this?

Upvotes: 6

Views: 396

Answers (3)

0x7ff
0x7ff

Reputation: 11

package main

import (
    "fmt"
    "time"
)

func main() {
    a := make(chan string)

    go func() {
        for {
            select {
            case v:= <-a:
                fmt.Print(v)
            }
        }
    }()

    a <- "Hello1\n"
    a <- "Hello2\n"
    a <- "Hello3\n"
    a <- "Hello4\n"

    time.Sleep(5*time.Second) 
}

Upvotes: 0

uselpa
uselpa

Reputation: 18917

<-a

gets a value from the channel, destructively. So in your code you get two values, one in the select statement, and one to print. The one received in the select statement is not bound to any variable, and is therefore lost.

Try

select {
    case val := <-a:
        fmt.Print(val)

instead, to get only one value, bind it to variable val, and print it out.

Upvotes: 7

Daniel
Daniel

Reputation: 38781

In the uppermost snippet, you're pulling two values from the channel for each loop. One in the select statement and one in the print statement.

Change

        select {
        case <-a:
            fmt.Print(<-a)

To

        select {
        case val := <-a:
            fmt.Print(val)

http://play.golang.org/p/KIADcwkoKs

Upvotes: 12

Related Questions