0

Why following codes always return 2,1, not 1,2.

func test(x int, c chan int) {
   c <- x
}

func main() {
    c := make(chan int)
    go test(1, c)
    go test(2, c)
    x, y := <-c, <-c // receive from c
    fmt.Println(x, y)
}
hzyu
  • 72
  • 8
  • 4
    By chance only. – Volker Jun 09 '18 at 07:24
  • It seems there is greater chance that first return 2 and then return 1 – hzyu Jun 09 '18 at 07:38
  • 4
    The order in which goroutines are scheduled is non-deterministic. In a specific environment (e.g. OS, Go version, GOMAXPROCS etc.) it may be that you always _observe_ the same order, but you cannot rely on this. The order is not specified by the language spec. If you need a specific execution order, you have to take care of it by explicit synchronization. – icza Jun 09 '18 at 08:59

1 Answers1

2

If you want to know what the order is, then let your program include ordering information

This example uses a function closure to generate a sequence

The channel returns a struct of two numbers, one of which is a sequence order number

The sequence incrementer should be safe across go routines as there is a mutex lock on the sequence counter

package main

import (
    "fmt"
    "sync"
)

type value_with_order struct {
    v     int
    order int
}

var (
    mu sync.Mutex
)

func orgami(x int, c chan value_with_order, f func() int) {
    v := new(value_with_order)
    v.v = x
    v.order = f()
    c <- *v
}
func seq() func() int {
    i := 0
    return func() int {
        mu.Lock()
        defer mu.Unlock()
        i++
        return i
    }
}

func main() {

    c := make(chan value_with_order)
    sequencer := seq()
    for n := 0; n < 10; n++ {
        go orgami(1, c, sequencer)
        go orgami(2, c, sequencer)
        go orgami(3, c, sequencer)
    }
    received := 0
    for q := range c {
        fmt.Printf("%v\n", q)
        received++
        if received == 30 {
            close(c)
        }
    }

}

second version where the sequence is called from the main loop to make the sequence numbers come out in the order that the function is called

package main

import (
    "fmt"
    "sync"
)

type value_with_order struct {
    v     int
    order int
}

var (
    mu sync.Mutex
)

func orgami(x int, c chan value_with_order, seqno int) {
    v := new(value_with_order)
    v.v = x
    v.order = seqno
    c <- *v
}
func seq() func() int {
    i := 0
    return func() int {
        mu.Lock()
        defer mu.Unlock()
        i++
        return i
    }
}

func main() {

    c := make(chan value_with_order)
    sequencer := seq()
    for n := 0; n < 10; n++ {

        go orgami(1, c, sequencer())
        go orgami(2, c, sequencer())
        go orgami(3, c, sequencer())
    }
    received := 0
    for q := range c {
        fmt.Printf("%v\n", q)
        received++
        if received == 30 {
            close(c)
        }
    }

}
Vorsprung
  • 32,923
  • 5
  • 39
  • 63
  • It seems the order is random – hzyu Jun 09 '18 at 14:11
  • hzyu the calling order is determined and each unique call will have a unique number. But the exact sequence number that is granted to each call will not necessarily be in order and the return sequence is clearly never going to be in order. If you need the calling sequence to be in order then set the sequence number in the same go routine (the main) . I'll add a second version that does this - call the sequence before the go routine – Vorsprung Jun 09 '18 at 14:49