I was studying Go concurrency pattern.
One pattern I am not sure is: Daisy Chain https://talks.golang.org/2012/concurrency.slide#39
It's very hard for me to understand the control flow of the code.
Can someone explain to me ?
package main
import (
"fmt"
)
func f(left, right chan int) {
left <- 1 + <-right
}
func main() {
const n = 10000
leftmost := make(chan int)
right := leftmost //point B: what does these do ?
left := leftmost
for i := 0; i < n; i++ {
right = make(chan int)
go f(left, right)
left = right //point A
}
go func(c chan int) { c <- 1 }(right)
fmt.Println(<-leftmost)
}
Conclusion:
the flow of channel going from right to left. It is good practice to write
func f(left chan<- int, right <-chan int)
rather than original function signature as above.
'chain reaction' does not start until c <- 1, when signal 1 is sent to right most channel, reaction goes all the way to left most end. Print out 10001.
The reason is go channel block 'read' until received channel receive signal.
@Rick-777 shows how to use array like structure for easy understanding. Since each go coroutine is just around 6k big. It's not a bad idea to make 10k channel.
I clean up some code around Point B, for channel initialization. Here is the source code: http://play.golang.org/p/1kFYPypr0l
VonC has already given a direct answer. Here are some further remarks.
A slightly tidied-up version is in the playground, the difference being that the channels passed as parameters have their direction specified explicitly, ie. <-chan
and chan<-
. It's good practice to do this because the compiler can catch more mistakes for you.
An alternative and equivalent program that has a daisy-chain of n
goroutines can be written using an array of channels instead. This allocates the same total number of channels using fewer lines of code. See playground:
package main
import (
"fmt"
)
func f(left chan<- int, right <-chan int) {
left <- 1 + <-right
}
func main() {
const n = 10000
// first we construct an array of n+1 channels each being a 'chan int'
var channels [n+1]chan int
for i := range channels {
channels[i] = make(chan int)
}
// now we wire n goroutines in a chain
for i := 0; i < n; i++ {
go f(channels[i], channels[i+1])
}
// insert a value into the right-hand end
go func(c chan<- int) { c <- 1 }(channels[n])
// pick up the value emerging from the left-hand end
fmt.Println(<-channels[0])
}
I hope you can see now how the original program is equivalent to this program. There is one minor difference: the original program does not create any channel array, so uses just a little less memory.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With