golang: Make select statemet deterministic? - go

Let's look carefully at the Ticker example code in Go's time package:
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
done := make(chan bool)
go func() {
time.Sleep(10 * time.Second)
done <- true
}()
for {
select {
case <-done:
fmt.Println("Done!")
return
case t := <-ticker.C:
fmt.Println("Current time: ", t)
}
}
}
With the interval adjusted to 1 second for convenience, after running the example enough times, we see an instance where the current time is never printed (or it would have only printed 9 times rather than 10):
Current time: 2020-06-10 12:23:51.189421219 -0700 PDT m=+1.000350341
Done!
Current time: 2020-06-10 12:23:52.193636682 -0700 PDT m=+1.000473686
Done!
Current time: 2020-06-10 12:23:53.199688564 -0700 PDT m=+1.000322824
Done!
Current time: 2020-06-10 12:23:54.204380186 -0700 PDT m=+1.000420293
Done!
Current time: 2020-06-10 12:23:55.21085129 -0700 PDT m=+1.000266810
Done!
Done!
Current time: 2020-06-10 12:23:57.220120615 -0700 PDT m=+1.000479431
Done!
Current time: 2020-06-10 12:23:58.226167159 -0700 PDT m=+1.000443199
Done!
Current time: 2020-06-10 12:23:59.231721969 -0700 PDT m=+1.000316117
Done!
When both the done and ticker.C channels are ready concurrently, we enter the realm of Go nondeterministic behavior:
A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.
I understand Go's design rationale for why select is non-deterministic. It mostly boils down to a problem the language does not venture to solve because doing so is generally hard and may lead users to write unknowingly racy code, and thus prioritized select and exercise left to the reader.
Let's assume that, for whatever reason, I'd like to ensure all pending ticks are consumed prior to winding down the program and printing Done!. Is there a general transformation that can be applied to this simple example to make it deterministic?
I tried adding another signal channel:
func main() {
ticker := time.NewTicker(time.Second)
stop := make(chan bool)
done := make(chan bool)
tick := make(chan time.Time)
go func() {
time.Sleep(1 * time.Second)
stop <- true
}()
go func() {
for t := range tick {
fmt.Println("Current time: ", t)
}
done <- true
}()
for {
select {
case <-stop:
ticker.Stop()
close(tick)
case t := <-ticker.C:
tick <- t
break
case <-done:
fmt.Println("Done!")
return
}
}
}
But it seems to preform worse...
Current time: 2020-06-10 13:23:20.489040642 -0700 PDT m=+1.000425216
Done!
Current time: 2020-06-10 13:23:21.495263288 -0700 PDT m=+1.000338902
Done!
Current time: 2020-06-10 13:23:22.501474055 -0700 PDT m=+1.000327127
Done!
Current time: 2020-06-10 13:23:23.503531868 -0700 PDT m=+1.000244398
Done!
Current time: 2020-06-10 13:23:24.510210786 -0700 PDT m=+1.000420955
Done!
Current time: 2020-06-10 13:23:25.516500359 -0700 PDT m=+1.000460986
Done!
Done!
Current time: 2020-06-10 13:23:27.527077433 -0700 PDT m=+1.000375330
Done!
Current time: 2020-06-10 13:23:28.533401667 -0700 PDT m=+1.000470273
Done!
panic: send on closed channel
goroutine 1 [running]:
main.main()
/home/dcow/Desktop/ticker-go/main2.go:29 +0x22f
Current time: 2020-06-10 13:23:30.547554719 -0700 PDT m=+1.000399602
Done!
Current time: 2020-06-10 13:23:31.55416725 -0700 PDT m=+1.000443683
Done!
Current time: 2020-06-10 13:23:32.56041176 -0700 PDT m=+1.000436364
Done!
Done!
Current time: 2020-06-10 13:23:34.572550584 -0700 PDT m=+1.000445593
Done!
Current time: 2020-06-10 13:23:35.578672712 -0700 PDT m=+1.000357330
Done!
Done!
Current time: 2020-06-10 13:23:37.590984117 -0700 PDT m=+1.000447504
Done!
We can't guarantee that we won't receive the stop message at the same time as we receive the final tick, so we've just shuffled the problem around to something that panics when it behaves "incorrectly" (which is marginally better than doing so silently). If we niled the tick channel, we'd devolve to the original case. And we still have cases where no tick is printed at all likely because it's possible we close the timer before it ever has a chance to fire..
How about a ready channel?
func main() {
ticker := time.NewTicker(time.Second)
tick := make(chan time.Time)
ready := make(chan bool, 1)
stop := make(chan bool)
done := make(chan bool)
go func() {
time.Sleep(1 * time.Second)
<-ready
stop <- true
}()
go func() {
for t := range tick {
fmt.Println("Current time: ", t)
}
done <- true
}()
for {
select {
case <-stop:
ticker.Stop()
close(tick)
case t := <-ticker.C:
select {
case ready<-true:
break
default:
}
tick <- t
break
case <-done:
fmt.Println("Done!")
return
}
}
}
This seems to work. It's somewhat involved with the addition of 3 new channels and an additional go routine, but it hasn't failed thus far. Is this pattern idiomatic in go? Are there general form strategies for applying this type of transformation in scenarios where you want to prioritize one of the select cases? Most advice I've come across is related to sequential and nested selects which don't really solve the problem.
Alternatively, is there a way to say "give me the list of ready channels so I can pick the order in which I process them"?
Edit:
Adding some clarifying remarks: I'm not interested in preserving ordering of concurrent operations. I agree that's a silly endeavor. I simply want to be able know if a selection of channels are ready to be processed and provide my own logic dictating what to do when multiple channels are ready concurrently. I'm essentially interested in a Go analog to POSIX select. And/or I'm interested in literature describing or common knowledge surrounding a generalized "convert non-deterministic select to deterministic select in Go" pattern.
e.g. Do people use the heap package and deposit data into a priority queue and ultimately read from that? Is there an x/reflect style package that implements a prioritized select using unsafe? Is there some simple pattern like, "Convert all selects with a single channel that should take priority into a dual channel style and forward the "done" request the the producer which in turn should terminate and close their channel then block on a channel range loop (kinda like my working solution)? Actually, lock on a shared condition variable for reasons x, y. etc..

If you need to pick one channel over another when both are enabled, then you can do a nested select. This will pick the high priority one over the low priority one if both channels are enabled at the beginning of select:
select {
case <-highPriority:
// Deal with it
default:
select {
case <-lowPriority:
// low priority channel
default:
}
}
If you have N channels with a priority ranking, then you can try selecting in a loop:
for _,channel:=range channels {
select {
case <-channel:
//
default:
}
}
This of course will be an approximation of what you need because it'll miss channel state changes that happen while it is looping. But it will prioritize channels based on their state at the beginning of the for loop.
Then there is reflect.Select, but that will not prioritize.

Unless the application has some known ordering between ready state of the ticker and done channels, it's impossible to ensure that the application processes the values from the channels in the order that the values are sent.
The application can ensure that values queued in ticker.C are received before a value from done by using nested select statements.
for {
select {
case t := <-ticker.C:
fmt.Println("Current time: ", t)
default:
// ticker.C is not ready for commination, wait for both
// channels.
select {
case <-done:
fmt.Println("Done!")
return
case t := <-ticker.C:
fmt.Println("Current time: ", t)
}
}
}
If the done communication is executed before a ready <-ticker.C communication in the inner select, then the two channels entered the ready state at almost the same time. Unless there's a requirement not stated in the question, this shouldn't make a difference to the application.
The application can nest a third select to give receive on ticker.C one last opportunity to execute before the function returns. This approach gives priority to the ticker when the two channels enter the ready state at almost the same time. I mention this for completeness, not because I recommend it. As I said in the previous paragraph, the first snippet of code in this answer should be good enough.
for {
select {
case t := <-ticker.C:
fmt.Println("Current time: ", t)
default:
// ticker.C is not ready for commination, wait for both
// channels.
select {
case <-done:
// Give communication on <-ticker.C one last
// opportunity before exiting.
select {
case t := <-ticker.C:
// Note that the ticker may have entered
// the ready state just after the done channel
// entered the state.
fmt.Println("Current time: ", t)
default:
}
fmt.Println("Done!")
return
case t := <-ticker.C:
fmt.Println("Current time: ", t)
}
}
}

Related

Concurrency in Go loop polling case branch not hit

I am implementing a very simple concurrent program in Go. There are 2 channels todo and done that are used for signaling which task is done. There are 5 routines that are executed and each one require its own time to complete. I would like to see every 100ms the status of what is happening.
However I tried but the polling branch case <-time.After(100 * time.Millisecond): seems that is never been called. It is called sometimes (not in a consisted way) if I reduce the time to something less than 100ms.
My understanding is that go func executes the method in a separate Go scheduler thread. I do not understand therefore why the case of the polling is never hit. I tried to move the specific case branch before/after the other but nothing changed.
Any suggestions?
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func concurrent(id int, done chan int, todo chan int) {
for {
// doing a task
t := randInt(50, 100)
time.Sleep(time.Duration(t) * time.Millisecond)
done <- id
// redo again this task
t = randInt(50, 100)
time.Sleep(time.Duration(t) * time.Millisecond)
todo <- id
}
}
func randInt(min int, max int) int {
return (min + rand.Intn(max-min))
}
func seedRandom() {
rand.Seed(time.Now().UTC().UnixNano())
}
func main() {
seedRandom()
todo := make(chan int, 5)
done := make(chan int, 5)
for i := 0; i < 5; i++ {
todo <- i
}
timeout := make(chan bool)
go func() {
time.Sleep(1 * time.Second)
timeout <- true
}()
var mu sync.Mutex
var output []int
loop:
for {
select {
case <-time.After(100 * time.Millisecond):
//this branch is never hit?
fmt.Printf("\nPolling status: %v\n", output)
case <-timeout:
fmt.Printf("\nDing ding, time is up!\n")
break loop
case id := <-done:
mu.Lock()
output = append(output, id)
fmt.Printf(".")
mu.Unlock()
case id := <-todo:
go concurrent(id, done, todo)
}
}
}
Update After following the answers I created this version in Go Playgound: https://play.golang.org/p/f08t984BdPt. That works as expected
you are creating 5 goroutines (func concurrent) and in your select case using the todo channel and this channel is being used in concurrent function so you end up creating a lot of goroutines
func concurrent(id int, done chan int, todo chan int) {
for {
// doing a task
t := randInt(50, 100)
time.Sleep(time.Duration(t) * time.Millisecond)
done <- id
// redo again this task
t = randInt(50, 100)
time.Sleep(time.Duration(t) * time.Millisecond)
by doing this call you are re-crating the go-routime
todo <- id
}
}
when I ran your code I got "runtime.NumGoroutine()"
"number of goRoutines still running 347"
as you are implementing the time.After(100 * time.Millisecond) inside the for loop it gets reset every time some other case gets hit and in your case
case id := <-todo: && id := <-done: will always get hit within 100 Milliseconds that's why you didn't get the expected output (from how your code is now i would say that the number of go-routines would increase exponentially and each of em would be waiting to send value to done and few on todo channel so your loop wont get enough time(100 ms) to wait on time.After)
loop:
for {
select {
case <-time.After(100 * time.Millisecond): ->this will always get reset ( we can use time.Ticker as it will create a single object that will signal for each and every 100ms https://golang.org/pkg/time/#NewTicker
//this branch is never hit?
fmt.Printf("\nPolling status: %v\n", output)
case <-timeout:
fmt.Printf("\nDing ding, time is up!\n")
break loop
case id := <-done: -> **this will get called**
//the mutex call is actually not very usefull as this only get called once per loop and is prefectly thread safe in this code
mu.Lock()
output = append(output, id)
fmt.Printf(".")
mu.Unlock()
case id := <-todo: -> **this will get called**
go concurrent(id, done, todo)
}
}
}
https://play.golang.org/p/SmlSIUIF5jn -> I have made some modifications to make your code work as expected..
try referring this to get a better understanding of golang channels and goroutine
https://tour.golang.org/concurrency/1
time.After(100*time.Millisecond) creates a brand new channel, with a brand new timer, which starts at the moment that function is called.
So, in your loop :
for {
select {
// this statement resets the 100ms timer each time you execute the loop :
case <-time.After(100*time.Millisecond):
...
Your branch never gets hit because, with 5 goroutines sending signals within less than 100ms on one of the other cases, this time.After(100ms) never reaches completion.
You need to choose a way to keep the same timer between iterations.
Here is one way to adapt your time.After(...) call :
// store the timer in a variable *outside* the loop :
statusTimer := time.After(100*time.Millisecond)
for {
select {
case <-statusTimer:
fmt.Printf("\nPolling status: %v\n", output)
// reset the timer :
statusTimer = time.After(100*time.Millisecond)
case <-timeout:
...
Another way is, as #blackgreen suggests, to use a time.Ticker :
statusTicker := time.NewTicker(100*time.Millisecond)
for {
select {
case <-statusTicker.C:
fmt.Printf("\nPolling status: %v\n", output)
case <-timeout:
...
side notes
a. if the output slice is not shared with other goroutines, you don't need a mutex around its access :
for {
select {
case <-statusTicker.C:
fmt.Printf("\nPolling status: %v\n", output)
...
case i <-done:
// no race condition here : all happens within the same goroutine,
// the 'select' statement makes sure that 'case's are executed
// one at a time
output = append(output, id)
fmt.Printf(".")
b. For your timeout channel :
Another generic way to "signal" that some event occurred with a channel is to close the channel instead of sending a value on it :
// if you don't actually care about the value you send over this channel :
// you can make it unbuffered, and use the empty 'struct{}' type
timeout := make(chan struct{})
go func(){
// wait for some condition ...
<-time.After(1*time.Second)
close(timeout)
}()
select {
case <-statusTimer:
...
case <-timeout: // this branch will also be taken once timeout is closed
fmt.Printf("\nDing ding, time is up!\n")
break loop
case ...
The bug you will avoid is the following : suppose you want to use that timeout channel in two goroutines
if you send a value over the timeout channel, only one goroutine will get signaled - it will "eat up" the value from the channel, and the other goroutine will only have a blocking channel,
if you close the channel, both goroutines will correctly "receive" the signal
In absence of a default case, when multiple cases are ready, it executes one of them at random. It's not deterministic.
To make sure the case runs, you should run it in a separate goroutine. (In that case, you must synchronize accesses to the output variable).
Moreover you say "I would like to see every 100ms", but time.After sends on the channel only once.
To execute the case periodically, use <-time.NewTicker(100 * time.Millis).C instead.
var mu sync.Mutex
var output []int
go func() {
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ticker.C:
// TODO: must synchronize access
fmt.Printf("\nPolling status: %v\n", output)
case <-timeout:
return
}
}
}()
loop:
for {
select {
// other cases
}
}

About the accuracy of the time.Timer

package main
import (
"time"
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
TestTicker(wg)
wg.Wait()
}
func TestTicker(wg sync.WaitGroup) {
calDuration := func(duration time.Duration) time.Duration {
now := time.Now()
return now.Truncate(duration).Add(duration).Sub(now)
}
go func(){
t := time.NewTimer(calDuration(time.Minute))
for {
<-t.C
fmt.Println(time.Now())
t.Reset(calDuration(time.Minute))
}
wg.Done()
}()
}
It sometimes happens to tick twice a minute as the duration may shrink. It's really strange. Could somebody help me. Thanks
I simply use waitgroup to hold the main function while calling TestTicker.
I'm running the test code on my MacOS
2018-07-19 14:36:00.003887996 +0800 CST m=+24.916092657
2018-07-19 14:37:00.002985076 +0800 CST m=+84.917119245
2018-07-19 14:38:00.001214551 +0800 CST m=+144.917278207
2018-07-19 14:39:00.000418561 +0800 CST m=+204.918411736
2018-07-19 14:39:59.999490194 +0800 CST m=+264.919412884
2018-07-19 14:40:00.000167519 +0800 CST m=+264.920090231
2018-07-19 14:40:59.99914446 +0800 CST m=+324.920996684
2018-07-19 14:41:00.000247228 +0800 CST m=+324.922099488
The timer accuracy can vary depending on your OS, hardware and CPU load. Virtual Machines seem particularly bad at providing accurate timers (see https://github.com/golang/go/issues/14410). Unfortunately, you do not mention in what environment you're running this code.
If you can live with the inaccuracies, and still need your code do do something at about a full minute, your code breaks because when the interval is too short (14:39:59.999490194 is only 500µs short of 14:40), calDuration will make it wait that few microseconds until the next full minute. In order to fix this you need to use Duration.Round instead of Duration.Truncate.
Also do not forget that t.C returns the time at which the timer fired, so you need to use this value in your call to calDuration (this also saves you a costly syscalls).
func TestTicker(wg *sync.WaitGroup) {
calDuration := func(now time.Time, duration time.Duration) time.Duration {
return now.Round(duration).Add(duration).Sub(now)
}
go func(){
t := time.NewTimer(calDuration(time.Now(), time.Minute))
for {
now := <-t.C
fmt.Println(now)
t.Reset(calDuration(now, time.Minute))
}
wg.Done()
}()
}
Another approach is to use time.Ticker from the standard library and issue an appropriate sleep before starting the ticker so that it ticks on a full minute:
func TestTicker(wg *sync.WaitGroup) {
go func(interval time.Duration) {
// wait until next time interval
now := time.Now()
time.Sleep(now.Truncate(interval).Add(interval).Sub(now))
// get time of first beat
now = time.Now()
// start the ticker
t := time.NewTicker(interval)
for {
fmt.Println(now)
now = <-t.C
}
wg.Done()
}(time.Minute)
}

Golang: Interrupting infinite polling having time.Sleep

I am using the following simple polling mechanism:
func poll() {
for {
if a {
device1()
time.Sleep(time.Second * 10)
} else {
sensor1()
time.Sleep(time.Second * 10)
}
}
}
I need to poll the device1 only if "a" is true otherwise poll the sensor1. Now here "a" will be set to true by clicking the button on UI which will be a random act.
But because of time.Sleep, delay get introduced while checking the condition.
Is there any way to immediately stop time.Sleep when I get the value for "a"?
what are the possible ways to implement such interrupts while polling in golang?
You cannot interrupt a time.Sleep().
Instead if you need to listen to other "events" while waiting for something, you should use channels and the select statements. A select is similar to a switch, but with the cases all referring to communication operations.
The good thing of select is that you may list multiple cases, all refering to comm. ops (e.g. channel receives or sends), and whichever operation can proceed will be chosen (choosing one randomly if multiple can proceed). If none can proceed, it will block (wait) until one of the comm. ops can proceed (unless there's a default).
A "timeout" event may be realized using time.After(), or a series of "continuous" timeout events (ticks or heartbeats) using time.Ticker.
You should change your button handler to send a value (the button state) on a channel, so receiving from this channel can be added to the select inside poll(), and processed "immediately" (without waiting for a timeout or the next tick event).
See this example:
func poll(buttonCh <-chan bool) {
isDevice := false
read := func() {
if isDevice {
device1()
} else {
sensor1()
}
}
ticker := time.NewTicker(1 * time.Second)
defer func() { ticker.Stop() }()
for {
select {
case isDevice = <-buttonCh:
case <-ticker.C:
read()
}
}
}
func device1() { log.Println("reading device1") }
func sensor1() { log.Println("reading sensor1") }
Testing it:
buttonCh := make(chan bool)
// simulate a click after 2.5 seconds:
go func() {
time.Sleep(2500 * time.Millisecond)
buttonCh <- true
}()
go poll(buttonCh)
time.Sleep(5500 * time.Millisecond)
Output (try it on the Go Playground):
2009/11/10 23:00:01 reading sensor1
2009/11/10 23:00:02 reading sensor1
2009/11/10 23:00:03 reading device1
2009/11/10 23:00:04 reading device1
2009/11/10 23:00:05 reading device1
This solution makes it easy to add any number of event sources without changing anything. For example if you want to add a "shutdown" event and a "read-now" event to the above solution, this is how it would look like:
for {
select {
case isDevice = <-buttonCh:
case <-ticker.C:
read()
case <-readNowCh:
read()
case <-shutdownCh:
return
}
}
Where shutdownCh is a channel with any element type, and to signal shutdown, you do that by closing it:
var shutdownCh = make(chan struct{})
// Somewhere in your app:
close(shutdownCh)
Closing the channel has the advantage that you may have any number of goroutines "monitoring" it, all will receive the shutdown (this is like broadcasting). Receive from a closed channel can proceed immediately, yielding the zero-value of the channel's element type.
To issue an "immediate read" action, you would send a value on readNowCh.

How to use time.After() instead of time.Sleep() to obtain interruptible pause

I have a program which periodically checks an external mailbox for messages and which has a user view which allows them to view messages and to terminate the program.
Stripped to minimal features it looks like this
package main
import (
"log"
"time"
)
func main() {
log.Println("Hello, playground")
quit := make(chan bool)
data := make(chan string)
go func() {
for {
select {
case <-quit:
log.Println("Quitting")
close(data)
return
case data <- fetch():
// Wait until e.g. exactly 0,10,20,30,40 or 50 mins past the hour
interval := time.Second * 5
now := time.Now()
time.Sleep(now.Truncate(interval).Add(interval).Sub(now))
}
}
}()
go func() {
time.Sleep(12 * time.Second) // actually user presses a "quit" button
quit <- true
}()
loop:
for {
select {
case info, ok := <-data:
if !ok {
break loop
}
log.Println("Fetched", info)
}
}
log.Println("Goodbye, playground")
}
func fetch() string {
log.Println("Fetching")
return "message"
}
You can run this in the Go Playground
Output is
2009/11/10 23:00:00 Hello, playground
2009/11/10 23:00:00 Fetching
2009/11/10 23:00:00 Fetched message
2009/11/10 23:00:05 Fetching
2009/11/10 23:00:05 Fetched message
2009/11/10 23:00:10 Fetching
2009/11/10 23:00:10 Fetched message
2009/11/10 23:00:15 Fetching
2009/11/10 23:00:15 Quitting
2009/11/10 23:00:15 Goodbye, playground
Notice that
"23:00:15 Fetching" is something I didn't expect.
The program quits at 23:00:15, not at 23:00:12 because it's sleeping.
The latter would be a problem in my program because it uses a 10 minute sleep between checking for messages. Delaying a quit for that long would make the program seem pretty unresponsive.
From this answer I have learned that you can use time.After() to create a loop delay that can be interrupted.
How should I best apply that to my program?
"23:00:15 Fetching" is something I didn't expect.
This is not surprising, this is the intended working. Quoting from Spec: Select statements:
Execution of a "select" statement proceeds in several steps:
For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement.
[...]
So the select statement evaluates the communication operations before it decides on which branch to proceed / execute.
This means that
case data <- fetch():
fetch() will be called, even if sending on data would not be possible and even if receiving from quit can proceed immediately.
Since you have the sleep in one of the case branches, it doesn't matter that quit becomes ready to receive from, checking that (and optionally deciding to go on that branch) has to wait until time.Sleep() –and the whole case branch– completes.
So the communication operation should be a receive operation from the channel returned by time.After(), and only call fetch() in the body of this case branch.
You can make it work like this:
for {
// Wait until e.g. exactly 0,10,20,30,40 or 50 mins past the hour
interval := time.Second * 5
now := time.Now()
delay := now.Truncate(interval).Add(interval).Sub(now)
select {
case <-quit:
log.Println("Quitting")
close(data)
return
case <-time.After(delay):
data <- fetch()
}
}
And now the output (try it on the Go Playground):
2009/11/10 23:00:00 Hello, playground
2009/11/10 23:00:05 Fetching
2009/11/10 23:00:05 Fetched message
2009/11/10 23:00:10 Fetching
2009/11/10 23:00:10 Fetched message
2009/11/10 23:00:12 Quitting
2009/11/10 23:00:12 Goodbye, playground

IF there is a small moment between the first iteration and the second iteration

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)
}
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.
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

Resources