1

In the code below, iterations are runned two times.
Is it possible that "test2 <- true" is runned at the moment which is just between the first iteration and the second iteration?
I mean, is there a change to send true to "test2" when the first iteration is ended and the second iteration is not started?

package main

import "log"
import "time"

func main() {
    test := make(chan bool, 1)
    test2 := make(chan bool, 1)

    go func() {
        for {
            select {
            case <-test:
                log.Println("test")
            case <-test2:
                log.Println("test2")
            }
        }
    }()

    test <- true
    time.Sleep(1)
    test2 <- true
    time.Sleep(1)
}
Nigiri
  • 3,469
  • 6
  • 29
  • 52

2 Answers2

4

Yes. Since your channels are buffered and can hold 1 value. the main execution flow can continue without your anonymous goroutine reading the value you send to the test channel, and it can send a value on the test2 channel before the goroutine wakes up and read the value on the test channel.

This is unlikely to happen, since you have a time.Sleep() call there to normally give time for the goroutine to execute, but there's no telling what'll happen in a corner case of your machine being very busy, being power suspended at an (un)lucky time or other things you didn't think about.

If your test channel was unbuffered, the test <- true statement would block until your goroutine received the value, and there would at least be no possibility for the goroutine to receive from test2 before receiving anything from the test channel.

nos
  • 223,662
  • 58
  • 417
  • 506
3

To add to nos' answer, you can simulate that case (where "test2 <- true" is run at the moment which is just between the first iteration and the second iteration") easily enough by making your first message reception (case <- test) wait one second.

case <-test:
    log.Println("test")
    time.Sleep(1 * time.Second)

By the time the anonymous goroutine wakes up, main() has already sent its two messages to the two buffered channel (buffer means non-blokcing for one message), and exited.
If main() exits, everything else, including the goroutine which was busy sleeping, stops.

See play.golang.org: the output would be:

2009/11/10 23:00:00 test

You wouldn't have the time to see test2.


In order to make sure your goroutine can process both message, you need:

  • main() to wait for said goroutine to finish. That is where the sync package comes into play, using (for instance, this isn't the only solution) a WaitGroup directive.

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        // Decrement the counter when the goroutine completes.
        defer wg.Done()
        ...
    }
    ... // end of main():
    // Wait for goroutine to complete.
    wg.Wait()
    
  • the goroutine to actually exit at some time (instead of being stuck in the for loop forever). See "In Go, does a break statement break from a switch/select?"

    loop:                                  <==========
    for {
        select {
        case <-test:
            log.Println("test")
            time.Sleep(1 * time.Second)
        case <-test2:
            log.Println("test2")
            break loop                     <==========
        }
    }
    

See play.golang.org: the message test2 is sent while the goroutine is sleeping after test1, but main() will wait (wg.Wait()), and the goroutine will have its chance to read and print test2 one second later.

The output is:

2009/11/10 23:00:00 test
2009/11/10 23:00:01 test2  // one second later
Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250