Golang channels, order of execution - go

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

Related

Do sending more than 8 element to a channel can result in deadlock?

package main
import (
"fmt"
"time"
)
func printCount(c chan int) {
num := 0
for num >= 0 {
num = <-c
fmt.Print(num, " ")
}
}
func main() {
a := []int{8, 6, 7, 5, 3, 0, 9, -1, 3, 4}
// If i use a := []int{8, 6, 7, 5, 3, 0, 9, -1} then it works fine
c := make(chan int)
go printCount(c)
for _, v := range a {
c <- v
}
time.Sleep(time.Second * 1)
fmt.Println("End of main")
}
I try to send element of the slice to the channel and print it using golang.
Now upto 8 element the program works fine.
As i am adding 9th element in slice then deadlock conditions occures dont know why. I used to solve that by using the waitgroup but still not working.
Sending a value through an (unbuffered) channel is always blocking, until it is received somewhere else.
I think your intention here is to send all values and then only print them if they are 0 or greater? You could alter your printCount like this:
func printCount(c chan int) {
for num := range c {
if num >= 0 {
fmt.Print(num, " ")
}
}
}
https://goplay.space/#m5eT9AYDH-Q
Hi just change c := make(chan int) to c := make(chan int, 100)
where 100 is highest capacity of your channel:
package main
import (
"fmt"
"time"
)
func printCount(c chan int) {
num := 0
for num >= 0 {
num = <-c
fmt.Print(num, " ")
}
}
func main() {
a := []int{8, 6, 7, 5, 3, 0, 9, -1, 3, 4}
// If i use a := []int{8, 6, 7, 5, 3, 0, 9, -1} then it works fine
c := make(chan int, 40)
go printCount(c)
for _, v := range a {
c <- v
}
time.Sleep(time.Second * 1)
fmt.Println("End of main")
}
result:
8 6 7 5 3 0 9 -1 End of main
The printCount goroutine exits if a negative value is processed.
More precisely, the value -1 is stopping it.
Here the printCount goroutine is the only goroutine that can read the channel values. When this goroutine exits no more writing are possible in the channel because the channel is unbuffered.
NB: The channel is unbuffered and thus cannot contains any value. A unbuffered channel is a golang idiom to concurrently safe pass values from a goroutine to another synchronously.
Solution
You should read the channel until no more element need to be processed.
No more need to use time.Sleep(time.Second * 1), the printCount is now blocking the main goroutine until finished !
package main
import (
"fmt"
"time"
)
func printCount(c chan int) {
// exits if the channel is empty (or unbuffered) and closed.
for num := range c {
if num >= 0 {
fmt.Print(num, " ")
}
}
}
func main() {
a := []int{8, 6, 7, 5, 3, 0, 9, -1, 3, 4}
c := make(chan int)
go func(){
// close the channel after sending the last value
defer close(c)
for _, v := range a {
c <- v
}
}()
printCount(c)
fmt.Println("End of main")
}

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.

understand go unbuffered channel example

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).

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