Before reading this article, we need to understand the concept of blocking
Pause during execution to wait for a certain condition to trigger, we call it blocking
In Go, we have two ways to make a channel, namely buffered and unbuffered
The buffer channel is created as make(chan TYPE, SIZE)
For example, make(chan int,3) is to create an int type channel with a buffer size of 3
The unbuffer channel is created as make(chan TYPE)
For example, make(chan int) is to create a non-buffered channel of type int
The difference between non-buffered channel and buffered channel
Non-buffered channel, channel sending and receiving actions occur simultaneously
For example, ch := make(chan int) , if no goroutine reads the receiver <-ch , then the sender ch<- will be blocked all the time
Buffer channel is similar to a queue. Only when the queue is full can blockage be sent.
Code Demo
Non-buffered channel
package main import ( "fmt" "time" ) func loop(ch chan int) { for { select { case i := <-ch: ("this value of unbuffer channel", i) } } } func main() { ch := make(chan int) ch <- 1 go loop(ch) (1 * ) }
There will be an error here fatal error: all goroutines are sleep - deadlock! It is because ch<-1 is sent, but there is no receiver at the same time, so it blocks
But if we put ch <- 1 under go loop(ch), the program will run normally
Buffer channel
Blocking will only occur when the channel's buffer is used up
package main import ( "fmt" "time" ) func loop(ch chan int) { for { select { case i := <-ch: ("this value of unbuffer channel", i) } } } func main() { ch := make(chan int,3) ch <- 1 ch <- 2 ch <- 3 ch <- 4 go loop(ch) (1 * ) }
Fatal error: all goroutines are sleep - deadlock! . This is because the size of the channel is 3 , and we have to stuff 4 data into it, so it will block
There are two solutions
Open the channel a little bigger, this is the easiest way and the most violent
Move the channel's message sender ch <- 1 these codes below go loop(ch) so that channel consumption in real time will not cause blockage
Supplement: 3 elegant Go channel usages
People who write Go must have heard this sentence from Rob Pike
Do not communicate by sharing memory; instead, share memory by communicating.
I believe that many friends, like me, always feel no benefits in actual applications and use them to use them. But from my personal experience, this is caused by the lack of complex scenes encountered when writing code and the unfamiliarity with the channel. So I hope this article can bring you some new ideas and a deeper understanding of Golang's elegant channel :)
Fan In/Out
The output of data sometimes requires fan-out/in (Fan In/Out), but when calling in functions, the interface often requires modification, and the upstream and downstream dependence on data is very high, so Fan In/Out is generally used to use channel, so that the pipeline similar to that in the shell can be easily implemented.
func fanIn(input1, input2 <-chan string) <-chan string { c := make(chan string) go func() { for { select { case s := <-input1: c <- s case s := <-input2: c <- s } } }() return c }
Synchronize Goroutine
The state is synchronized between two goroutines. For example, A goroutine needs to exit B goroutine. The general practice is as follows:
func main() { g = make(chan int) quit = make(chan bool) go B() for i := 0; i < 3; i++ { g <- i } quit <- true // There is no way to wait for B to exit, only Sleep ("Main quit") } func B() { for { select { case i := <-g: (i + 1) case <-quit: ("B quit") return } } } /* Output: 1 2 3 Main quit */
However, the main function cannot wait for B to exit properly, so B quit cannot print, and the program exits directly.
However, chan is the first object in Go, so chan can be passed into chan. Therefore, the above code can define quit as chan chan bool to control the synchronization of two goroutines
func main() { g = make(chan int) quit = make(chan chan bool) go B() for i := 0; i < 5; i++ { g <- i } wait := make(chan bool) quit <- wait <-wait //This way you can wait for B to exit ("Main Quit") } func B() { for { select { case i := <-g: (i + 1) case c := <-quit: c <- true ("B Quit") return } } } /* Output 1 2 3 B Quit Main Quit */
Distributed recursive call
In real life, what would you do if you were to chat with the US president?
The first step is to call friends in the United States, and they will also launch their own network of relationships, and then find people who may know the president of the United States, and so on until they find it.
This is the same in Kadmelia distributed system. If you need to obtain the target ID information, you will keep querying. Even if the query node does not have relevant information, it will return the closest node it feels until the ID is found or waits for the timeout.
OK, how to do this with Go?
func recursiveCall(ctx , id []byte, initialNodes []*node){ seen := map[string]*node{} //See node records request := make(chan *node, 3) //Set the request node channel // Enter the initial node go func() { for _, n := range initialNodes { request <- n } }() OUT: for { //Loop until data is found if data != nil { return } // select in new request, timeout and upper cancel request select { case n := <-request: go func() { // Send a new request response := (ctx, n, MethodFindValue, id) select { case <-(): case msg :=<-response: seen[responseToNode(response)] = n //Update the node information you have seen // Load new node for _, rn := range LoadNodeInfoFromByte(msg[PayLoadStart:]) { () _, ok := seen[()] () // I've seen it, skip this node if ok { continue } AddNode(rn) // Send new node to channel request <- rn } } } }() case <-(500 * ): break OUT // break to the outer layer, otherwise it just jumps to the outside of the loop case <-(): break OUT } } return }
At this time, the buffered channel is similar to a local queue, processing the required nodes, but the exquisite thing about this code is that the block operation here is selected and can be cancelled at any time, rather than waiting or understanding the length of the queue.
The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.