Elega
Elega

Reputation: 71

Goroutine execution order

I'm a beginner of golang and I'm learning the goroutine and channel. Intuitively, I consider a gorountine in golang is essentially a thread running independently. Therefore, if there are more than one goroutine, the execution order won't be guaranteed. Hence, the following code should output "ping" and "pong" in a random order. However, what I observed is that the program yields "ping" and "pong" in turn. Can anyone explain the reason for me? Any reply would be helpful. Thanks!

Another intriguing observation I found is that if I don't call sleep function inside the printer function, the program will output in a random order.

package main

import (
  "fmt"
  "time"
)

func pinger(c chan string) {
  for i := 0; ; i++ {
    c <- "ping"
  }
}

func printer(c chan string) {
  for {
    msg := <- c
    fmt.Println(msg)
    time.Sleep(time.Second * 1)
  }
}

func ponger(c chan string) {
  for i := 0; ; i++ {
    c <- "pong"
  }
}

func main() {
  var c chan string = make(chan string)

  go pinger(c)
  go ponger(c)
  go printer(c)

  var input string
  fmt.Scanln(&input)
}

Upvotes: 5

Views: 1204

Answers (2)

sberry
sberry

Reputation: 132018

To better understand consider the following statement and demo program:

  1. Sends on a blocked channel will be serviced in order of attempted send. I wasn't 100% sure about this but the demo program shows this behavior and this [ticket][1] mentions implementing this behavior over 2 years ago.

To really see this you should run this on your machine:

package main

import (
    "fmt"
    "time"
)

func main() {
    count := 10
    c := make(chan int)
    for i := 0; i < count; i++ {
        go func(i int) {
            c <- i + 1
        }(i)
    }
    for i := 0; i < count; i++ {
        fmt.Println("c", <-c)
    }

    fmt.Println()

    d := make(chan int)
    for i := 0; i < count; i++ {
        go func(i int) {
            d <- i + 1
        }(i)
        time.Sleep(time.Millisecond * 5)
    }
    for i := 0; i < count; i++ {
        fmt.Println("d", <-d)
    }

    var input string
    fmt.Scanln(&input)
}

Here is a playground, though it won't be as obvious what is happening because the first group (c) will always appear in a consistent (though non-sorted) order. The second group (d) will always appear in order on the playground or locally.

https://play.golang.org/p/JCVyVlFPRXS

These are the outputs from 3 runs locally

 1     2     3
----------------
c 2   c 10  c 10
c 1   c 4   c 2
c 6   c 1   c 1
c 3   c 2   c 4
c 4   c 3   c 3
c 5   c 7   c 5
c 8   c 5   c 8
c 7   c 6   c 7
c 9   c 8   c 9
c 10  c 9   c 6

d 1   d 1   d 1
d 2   d 2   d 2
d 3   d 3   d 3
d 4   d 4   d 4
d 5   d 5   d 5
d 6   d 6   d 6
d 7   d 7   d 7
d 8   d 8   d 8
d 9   d 9   d 9
d 10  d 10  d 10

Upvotes: 3

peterSO
peterSO

Reputation: 166588

The Go Programming Language Specification

Channel types

The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready.


You have an unbuffered channel: make(chan string).

msg := <- c (ready to receive) and c <- "ping" (ready to send): send and receive "ping".

msg := <- c (ready to receive) and c <- "pong" (ready to send): send and receive "pong".

Upvotes: 4

Related Questions