Golang频道输出顺序

func main() {
  messages := make(chan string)
  go func() { messages <- "hello" }()
  go func() { messages <- "ping" }()
  msg := <-messages
  msg2 := <-messages
  fmt.Println(msg)
  fmt.Println(msg2)

The above code consistently prints "ping" and then "hello" on my terminal. I am confused about the order in which this prints, so I was wondering if I could get some clarification on my thinking.

I understand that unbuffered channels are blocking while waiting for both a sender and a receiver. So in the above case, when these 2 go routines are executed, there isn't, in both cases,a receiver yet. So I am guessing that both routines block until a receiver is available on the channel.

Now... I would assume that first "hello" is tried into the channel, but has to wait... at the same time, "ping" tries, but again has to wait. Then

msg := <- messages

shows up, so I would assume that at that stage, the program will arbitrarily pick one of the waiting goroutines and allow it to send its message over into the channel, since msg is ready to receive.

However, it seems that no matter how many times I run the program, it always is msg that gets assigned "ping" and msg2 that gets assigned "hello", which gives the impression that "ping" always gets priority to send first (to msg). Why is that?

It’s not about order of reading a channel but about order of goroutines execution which is not guaranteed.

Try to ‘Println’ from the function where you are writing to the channel (before and after writing) and I think it should be in same order as reading from the channel.

In Golang Spec Channels order is described as:-

Channels act as first-in-first-out queues. For example, if one goroutine sends values on a channel and a second goroutine receives them, the values are received in the order sent.

It will prints which value is available first to be received on other end. If you wants to synchronize them use different channels or add wait Groups.

package main

import (
    "fmt"
)

func main() {
  messages1 := make(chan string)
  messages2 := make(chan string)
  go func(<-chan string) {
        messages2 <- "ping" 
    }(messages2)
  go func(<-chan string) {
        messages1 <- "hello" 
    }(messages1)
  fmt.Println(<-messages1)
  fmt.Println(<-messages2)
}

If you see you can easily receive any value you want according to your choice using different channels.

Go playground

I just went through this same thing. See my post here: Golang channels, order of execution

Like you, I saw a pattern that was counter-intuitive. In a place where there actually shouldn't be a pattern. Once you launch a go process, you have launched a thread of execution, and basically all bets are off at that point regarding the order that the threads will execute their steps. But if there was going to be an order, logic tells us the first one called would be executed first.

In actual fact, if you recompile that program each time, the results will vary. That's what I found when I started compiling/running it on my local computer. In order to make the results random, I had to "dirty" the file, by adding and removing a space for instance. Then the compiler would re-compile the program, and then I would get a random order of execution. But when compiled in the go sandbox, the result was always the same.

When you use the sandbox, the results are apparently cached. I couldn't get the order to change in the sandbox by using insignificant changes. The only way I got it to change was to issue a time.Sleep(1) command between the launching of the go statements. Then, the first one launched would be the first one executed every time. I still don't think I'd bet my life on that continuing to happen though because they are separate threads of execution and there are no guarantees.

The bottom line is that I was seeing a deterministic result where there should be no determinism. That's what stuck me. I was fully cleared up when I found that the results really are random in a normal environment. The sandbox is a great tool to have. But it's not a normal environment. Compile and run your code locally and you will see the varying results you would expect.