6

I am a beginner of Golang, I read from the official spec of select that I will do uniform pseudo-random when more of communications can proceed, but when I tried the following code

package main

import (
    "fmt"
)

func main() {

    // For our example we'll select across two channels.
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        for {
            c1 <- "one"
        }
    }()
    go func() {
        for {
            c2 <- "two"
        }
    }()

    for i := 0; i < 100; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}

It always print 'received two', seems not to be a random result, so where am I wrong?

The code can be test here.

kemiya
  • 460
  • 2
  • 5
  • 16
  • It seems that the first func never gets a chance to run. And I doubt if that is expected behavior. However I am on mobile so I can provide no more information. – leaf bebop Jan 16 '18 at 04:15
  • how many CPU cores? or try run more times(eg 10000?) – zzn Jan 16 '18 at 04:27

2 Answers2

6

the problem is you run it on go playground,, On the Go Playground, GOMAXPROCS is 1,, This means one goroutine is executed at a time, and if that goroutine does not block, the scheduler is not forced to switch to other goroutines.

So, you should run it in your local machine, to see the real results

When you run it locally, most likely GOMAXPROCS will be greater than 1 as it defaults to the number of CPU cores available (since Go 1.5). So it doesn't matter if you have a goroutine executing an endless loop, another goroutine will be executed simultaneously, which will be the main(), and when main() returns, your program terminates; it does not wait for other non-main goroutines to complete

Fendi jatmiko
  • 2,567
  • 1
  • 9
  • 15
  • 2
    I tried it in my local machine, and it did show random result. But I feel a little confused about the explanation, the GOMAXPROCS is actually 1 on the Go Playground, but the scheduler should always switch between main goroutine and other goroutine cause both `c1` and `c2` are not buffered, isn't it? when a message send to channel, it should block for a very little while so main goroutine can receive it, but why can't it switch to another sender goroutine? – kemiya Jan 16 '18 at 06:37
  • Process switching is relatively slow so the whole of the chan2 loop is executed before the processor switches to the other process. If you add some `time.Sleep(time.Millisecond)` into the loop you will see a different behavior on the playground. https://play.golang.org/p/-J0sTQ_b-Qa Although it is not very random, it's alternating very regularly, with a step size of 2 which makes me wonder... – Christian Jan 16 '18 at 13:47
  • No reference to https://stackoverflow.com/a/36706499/2541573 in this answer, although it borrows heavily from it. Don't forget to give attribution where it's due. – jub0bs Aug 07 '19 at 16:01
1

Depending on the Go version used, it might be; see this issue. It should be fixed in Go 1.10 (regarding this one, it is fixed in Go 1.9.2 but I have not tested).

Kaveh Shahbazian
  • 13,088
  • 13
  • 80
  • 139
  • My Go version is 1.8.3, according to the issue, it should works well perfectly on this version, and I looked into the `fastrand()` method source code of version 1.7/1.8/1.9, and it should work perfectly cause no tricky `fastrand()` method was used by Golang Team. – kemiya Jan 16 '18 at 07:22
  • I have Go 1.9.2 on my machine and tested your code. The select statement acts (relatively) fair with this setup. In that thread it is mentioned that, this should work perfectly in Go 1.8.3. Just an observation: calling `runtime.Gosched()` inside the main loop, makes it even more fair. I do not know exactly how the cooperative scheduler of Go decides on stealing from each goroutine, but `runtime.Gosched()` seems to force it to act more generously, although is it not considered a good practice. https://play.golang.org/p/VDij1jWucUe – Kaveh Shahbazian Jan 16 '18 at 07:48