understand go unbuffered channel example - go

official code example
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x := <-c
y := <-c
fmt.Println(x, y, x+y)
}
printed : -5, 17, 12
why not printed 17, -5, 12 ?
I think x should be 17

There's no defined order of which value gets written to the channel first, it's purely at the mercy of the scheduler. As all the example is doing is adding the results, it doesn't actually matter which way round the results turn up.

There is no guarantee in what order your go sum(...) goroutines will start and which will be the first to finish (and send to channel).

Related

Golang channels, order of execution

I'm learning Go, and have run across the following code snippet:
package main
import "fmt"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
c <- sum // send sum to c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int, 2)
go sum(a[0:3], c)
go sum(a[3:6], c)
x := <-c
y := <-c
// x, y := <-c, <-c // receive from c
fmt.Println(x, y)
}
Output:
-5 17
Program exited.
Can someone please tell me why the 2nd calling of the "sum" function is coming through the channel before the 1st one? It seems to me that the output should be:
17 -5
I've also tested this with an un-buffered channel and it also gives the same order of output. What am I missing?
You are calling the go routine in your code and you can't tell when the routine will end and the value will be passed to the buffered channel.
As this code is asynchronous so whenever the routine will finish it will write the data to the channel and will be read on the other side. In the example above you are calling only two go routines so the behavior is certain and same output is generated somehow for most of the cases but when you will increase the go routines the output will not be same and the order will be different unless you make it synchronous.
Example:
package main
import "fmt"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
c <- sum // send sum to c
}
func main() {
a := []int{7, 2, 8, -9, 4, 2, 4, 2, 8, 2, 7, 2, 99, -32, 2, 12, 32, 44, 11, 63}
c := make(chan int)
for i := 0; i < len(a); i = i + 5 {
go sum(a[i:i+5], c)
}
output := make([]int, 5)
for i := 0; i < 4; i++ {
output[i] = <-c
}
close(c)
fmt.Println(output)
}
The output for this code on different sample runs was
[12 18 0 78 162]
[162 78 12 0 18]
[12 18 78 162 0]
This is because the goroutines wrote the output asynchronously to the buffered channel.
Hope this helps.
When running the golang.org sandbox, I got the same result every time. As stated above. But when I ran the same snippet on in my own sandbox (on my computer), it sometimes changed the order of the threads. This is much more satisfactory. It shows I can't expect any particular order to thread execution, which is intuitive. I just couldn't figure out why I was getting the same order of execution, and it was the reverse of the order the threads were started. I think this was just luck of the draw on the golang.org's sandbox.
Goroutines are started asynchronously and they can write to channel in any order. It is easier to see if you modify your example a little bit:
package main
import (
"fmt"
"time"
)
func sum(a []int, c chan int, name string, sleep int) {
fmt.Printf("started goroutine: %s\n", name)
time.Sleep(time.Second * time.Duration(sleep))
sum := 0
for _, v := range a {
sum += v
}
fmt.Printf("about end goroutine: %s\n", name)
c <- sum // send sum to c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int, 2)
go sum(a[0:3], c, "A", 1)
go sum(a[3:6], c, "B", 1)
x := <-c
y := <-c
// x, y := <-c, <-c // receive from c
fmt.Println(x, y)
}
https://play.golang.org/p/dK4DT0iUfzY
Result:
started goroutine: B
started goroutine: A
about end goroutine: A
about end goroutine: B
17 -5

Receiving values from channels in Go

Why doesn't the last result returned from go sum(s[len(s)/2:], c)(the second one) be assigned to x?
The two <-cs confuse me.
This code is from A Tour of Go - [Channels].
package main
import "fmt"
func sum(s []int, c chan int) { // int is the return value type
sum := 0
for _, v := range s {
sum += v
}
c <- sum // Sends sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
Do you mean this line?
x, y := <-c, <-c
That's a "tuple assignment".
In this case, it would be equivalent to doing:
x := <-c
y := <-c
So the second value read from c would be assigned to y.
You can read more about it here:
https://golang.org/ref/spec#Assignments
About the order in which the values are actually written to c, since two independent goroutines are being triggered here:
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
The order in which they end up processing and writing to c is not "guaranteed", so you can expect either one of the values be assigned to x and y in different runs.
Since the values are being added to calculate the final result, this is not a problem in this case.

Go tutorial: Channels, Buffered Channels tutorial

I am going through Go's official tutorial and have difficulty understanding the difference between Channel and Buffered Channels. The links to the tutorials are https://tour.golang.org/concurrency/2 and https://tour.golang.org/concurrency/3
In the Channel tutorial, Channel c first received the sum of [7, 2, 8] which is 17 and then received the sum of [-9, 4, 0] which is -5. When reading from c, it first output -5 to x and then 17 to y, in LIFO order:
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
(The above output is -5 17 12)
In the Buffered Channel tutorial, the output is 1 2, in FIFO order:
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
Why are they different?
The chnnael c, in your 1st example of unbuffered channel, is not acting as LIFO.
Actually it is happening because of go routines. The go routines executes concurrently.
If you tweak your code to debug, add one extra line in sum to print the sum before sending to channel.
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
fmt.Println("slice:", s)
fmt.Println(sum)
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:2], c)
go sum(s[2:4], c)
go sum(s[4:6], c)
x, y, z := <-c, <-c, <-c // receive from c
fmt.Println(x, y, z, x+y+z)
}
The output is:
slice: [4 0]
4
slice: [7 2]
9
slice: [8 -9]
-1
4 9 -1 12
So, you can see that x receives the 1st number that was sent through channel.
Furthermore, unbuffered channels sends data directly to receiver.
If you wanna know about the architecture of channels in go, you can watch this talk of gophercon-2017. I found this talk very helpful.

get unexpect datas from channel using goroutine

A piece of code from tutorials on tour.golang.org
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c) //give 17
go sum(s[len(s)/2:], c) //give -5
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
The output result is always -5 17 12 which means the first goroutine is stably behind the second. As my understanding to multithreading and channel, the order of result should be the opposite in this simple context.When the goroutine's task become more complexed the order should be unexpectable.So how to explain this LIFO like issue?

gooroutine is have a priority or not?

Golang fish,Seeking explanation.
Goroutine is have a priority or not?
package main
import (
"fmt"
)
func sum(a []int, c chan int) {
var total int
for _, v := range a {
total += v
}
c <- total
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
// x, y := <-c, <-c
x := <-c
y := <-c
fmt.Println(x, y, x+y)
}
why the x is -5 y is 17,is not the first goroutine blocked?
if
go sum(a[:len(a)/2], c)
x := <-c
go sum(a[len(a)/2:], c)
y := <-c
this order is right. why。。。
In your first example, the output should be either -5 17 12 or 17 -5 12. Both goroutines are running concurrently (at the same time). The result of whichever goroutine finishes first will be stored in the variable x. The result of the other goroutine is stored in y. In order to better see that the goroutines are running concurrently, you can put a random timer inside the function sum(). This way, you should see the output change between different runs, because one goroutine randomly takes longer than the other:
package main
import (
"fmt"
"time"
"math/rand"
)
func sum(a []int, c chan int) {
time.Sleep(time.Duration(rand.Intn(1000000))) // wait for up to 1ms
total := 0
for _, v := range a {
total += v
}
c <- total
}
func main() {
rand.Seed(time.Now().Unix())
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x := <-c
y := <-c
fmt.Println(x, y, x+y)
}
In your second example, you are starting the first goroutine. Then you read from the channel c, which is a blocking operation (meaning it will wait until there's a result => the first goroutine is done). The output here is deterministic and will always be 17 -5 12.

Resources