8
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?

hundred_dolla_tea
  • 523
  • 1
  • 5
  • 14
  • What do you want. Do you wants to synchronize the channels so that `hello` will print first always ? – Himanshu Jun 02 '18 at 08:37
  • 2
    Your observations are correct, the execution order of the threads (in the case of golang, the go routines) is not designed to be predictable. If you want to control the execution flow in a specific fashion, perhaps this post (https://stackoverflow.com/questions/39818254/goroutines-order-of-execution) might help you. – Victor Jun 02 '18 at 13:31

4 Answers4

7

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.

Alexander Trakhimenok
  • 6,019
  • 2
  • 27
  • 52
6

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

Himanshu
  • 12,071
  • 7
  • 46
  • 61
  • "It does not depends on the value send on the channel first. It will prints which value is available first to be received on other end" is false. The values are always received in the order they are sent. "The kth receive on a channel with capacity C happens before the k+Cth send from that channel completes." https://golang.org/ref/mem#tmp_7 The value that is available first *is* the value that is sent first. – Peter Jun 02 '18 at 09:31
  • Ok the why is it so that the value send first is not printing first. Actually I also wants to know. Because I have seen many answers and everyone says that it does not matter. Well thanks I have edited my answer also looking at the link you have given. – Himanshu Jun 02 '18 at 09:43
  • @Himanshu as Alexander Trakhimenok points out in his answer, the order depends on how the goroutines are scheduled and then executed. This is, as far as I know, an implementation detail. You should not expect your goroutines to be executed in the same order in which you wrote them in your source code. E.g. `go a(); go b()` here the Go spec does not guarantee that `a` will run before `b`, maybe it will, but maybe it won't. This is up to the scheduler. If `b` runs first you will get the result that OP is seeing. `msg := <-messages` will result in `"ping"` and `msg2 := <-messages` in `"hello"`. – mkopriva Jun 02 '18 at 11:15
  • yes I know these things from many accusations about go routines. But why is it so that OP is getting same answer ? Well I have just given the answer In case OP wants to have a control on which message should prints first. – Himanshu Jun 02 '18 at 12:29
4

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.

mikekehrli
  • 332
  • 3
  • 10
0

I was confused when I first met this. but now I am clear about this. the reason cause this is not about channel, but for goroutine.

As The Go Memory Model mention, there's no guaranteed to goroutine's running and exit, so when you create two goroutine, you cannot make sure that they are running in order.

So if you want printing follow FIFO rule, you can change your code like this:

func main() {
    messages := make(chan string)
    go func() {
        messages <- "hello"
        messages <- "ping"
    }()
    //go func() { messages <- "ping" }()
    msg := <-messages
    msg2 := <-messages
    fmt.Println(msg)
    fmt.Println(msg2)
}
soolaugust
  • 245
  • 2
  • 10