I'm studying Go lang through 'A tour of Go', and it's hard to understand Go channel running sequence,
package main
import "fmt"
import "time"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
time.Sleep(1000 * time.Millisecond)
sum += v
}
c <- sum // send sum to c
}
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 // receive from c
fmt.Println(x, y, x+y)
fmt.Println("Print this first,")
}
If run above the code, I expected,
Print this first,
17 -5 12
Because, Go routine runs as non-blocking, a But, actually it prints,
17 -5 12
Print this first,
The other example that I found in internet,
package main
import "fmt"
type Data struct {
i int
}
func func1(c chan *Data ) {
fmt.Println("Called")
for {
var t *Data;
t = <-c //receive
t.i += 10 //increment
c <- t //send it back
}
}
func main() {
c := make(chan *Data)
t := Data{10}
go func1(c)
println(t.i)
c <- &t //send a pointer to our t
i := <-c //receive the result
println(i.i)
println(t.i)
}
Also, I expected, it prints "Called" first, but the result is
10
20
20
Called
What I am misunderstanding? please help me to understand Go routine and channel.
In your first example, x, y := <-c, <-c will block until it reads off c twice, and then assign values to x, y. Channels aside, you have an assignment, a print statement, then another print statement. Those are all synchronous things, and will happen in the order you state them in. There's no way the second print statement would print first.
The second one is because fmt.Println writes to STDOUT and println to STDERR. If you are consistent (say use println everywhere) then you see:
10
Called
20
20
That's cause there's a race between the first println(t.i) in the main and the println("Called") that's happening in the goroutine. I'm guessing with GOMAXPROCS set to 1, this will happen consistently. With GOMAXPROCS set to NumCPU, I get a mix of results, sometimes looking like the above, and sometimes like this:
10Called
20
20
Related
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
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.
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?
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.
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)
go sum(a[len(a)/2:], c)
go sum(a[:len(a)/2], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
x, y := <-c, <-c // receive from c
Why does this line always print the same result?
I think it should be 50/50 chance to print
17 -5 12
or
-5 17 12
I think the two go routines should be parallel
Thanks in advance!
package main
import "fmt"
import "time"
import "math/rand"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
c <- sum // send sum to c
}
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 // receive from c
fmt.Println(x, y, x+y)
}
I make the thread to sleep, but same thing happens. I am still confusing.
The assumption that it should be a 50/50 chance is incorrect in this situation. To be clear, I'm not suggesting it should always be one or the other but rather that you shouldn't expect it to change each time the program runs. The behavior is not necessarily random (or even pseudo-random).
A goroutine's guarantee is not that it will be scheduled at a random future time, but rather just that it will run at some time in the future. If the current implementation of the scheduler decided to put goroutines in a simple queue, it wouldn't automatically mean it's broken. For your particular code's case, if you stick a few time.Sleeps or fmt.Printfs in different places, you'd likely see the order change around sometimes.
I wonder if you're confusing Go's scheduler's selection of the next goroutine with the documented pseudo-random behavior of select when used with channels. The behavior there is defined to be random and it's correct to say the behavior should have a 50/50 chance.
As far as I've read, the choice of which goroutine the runtime will select is not random. That's not the same as saying it can't be random, but moreso that it's not supposed to be random.
The important thing is that your code shouldn't care about the order in which goroutines are scheduled. Whether it's always in order like a queue, or backwards (a stack), or random, or something else (likely the reality).