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
I found dry-run this program could be really helpful to understand it.
At first, after executing
leftmost := make(chan int)
right := leftmost
left := leftmost
leftmost
, left
, and right
are all referring to the same chan int
[chan int]
|
left, leftmost, right
Let's run some iterations for the for-loop.
i = 0
When we just enter the for loop,
[chan int]
|
left, leftmost, right
after executing right = make(chan int)
and go f(left, right)
.
[chan int] <-(+1)- [chan int]
| |
left, leftmost right
after executing left = right
[chan int] <-(+1)- [chan int]
| |
leftmost left, right
i = 1
When we just enter the for loop,
[chan int] <-(+1)- [chan int]
| |
leftmost left, right
after executing right = make(chan int)
and go f(left, right)
.
[chan int] <-(+1)- [chan int] <-(+1)- [chan int]
| | |
leftmost left right
after executing left = right
[chan int] <-(+1)- [chan int] <-(+1)- [chan int]
| |
leftmost left, right
I feel like two loops are enough to see the pattern:
chan int
and append it at the end of the "linked list of chan int
".n = 100000
loops, we created 100000
new chan int
, and the number of chan int
in the "linked list of chan int
will be 100001
.100001
chan int
means 100000
gaps between each pair of adjacent chan int
, and each gap means one +1
.Before the for loop, because all chan int
are acting as receivers and there is no pass-in value, so all chan int
will just wait.
After the for loop, we execute go func(c chan int) { c <- 1 }(right)
, then the 1
is passed into the "linked list of chan int
" and perform +1
on the value for 100000
times, so the final result to the leftmost
will be 100001
.
Things will be like when we pass 1
into the "linked list of chan int
":
[chan int] <-(+1)- [chan int] <-(+1)- ...... <-(+1)- [chan int] <- 1
| |
leftmost left, right
I created a leetcode playground holding all the code. You could try it here (https://leetcode.com/playground/gAa59fh3).