The article number originated in public links: Michael mo coding 】 【 mp.weixin.qq.com/s/QYmcsD-F5…
directory
- Background of the channel
- Basic usage of channel
- Channel Application Scenarios
- Channel implementation Principle
- Channel data structure
- Channel implementation
- Precautions for channel
- Small talk
- Welcome to join my official account [Maimocoding] pk big factory together
Background of the channel
Channel is the core type of Go. It is built into the Go language, and you don’t need to invoke it. You can think of it as a pipe. There is a saying in the Go language, “Goroutines that do business do not communicate through shared memory, but share data through a channel pipe.”
Together with Goroutine, another feature of Go, Channel provides an elegant and convenient solution for concurrent programming to handle concurrent scenarios.
Basic usage of channel
The basic usage of a channel is very simple. It provides three types: receive only, send only, and receive or send. So its syntax is:
chan<- struct{} // Only structs can be sent
<-chan struct{} // Can only receive structs from chan
chan string // Can receive and send
Copy the code
We call the Chan that can both send and receive two-way Chan, and the Chan that can only receive or send one-way Chan. Where “<-” means one-way chan, and if you can’t remember, I’ll give you a quick way to do it: the arrow always goes to the left, and the element type is always on the far right. If the arrow points to chan, it means that data can be stuffed into chan; If the arrow is away from chan, it means that Chan is spitting out data.
With the make keyword, we can initialize a chan, and the uninitialized chan has a zero value of nil. You can set its capacity. The second parameter is the size of the buffer pool, which can also be interpreted as storing data even if the chan is not consumed.
make(chan int.8)
Copy the code
If there is still data in the CHAN, receiving data from the CHAN will not block, and if the chan does not reach the queue capacity, storing data to the CHAN will not block, and vice versa.
One more thing to keep in mind: Nil is the zero of chan, a special kind of chan, and the send/receive caller to chan that is nil will always block.
Next, let’s use code to learn about the three types of chan
- Chan who can only receive data
Code sample
package main
import "fmt"
// A represents chan that can only receive data
func goChanA(a <-chan int) {
b := <-a
fmt.Println("Channal [a], which can only receive data, received the data value as", b)
}
func main(a) {
ch := make(chan int.2)
go goChanA(ch)
// Write data to ch
ch <- 2
time.Sleep(time.Second)
}
Copy the code
Result Channal [A], which can only receive data, received the data value 2
- Chan that can only send data
Code sample
package main
import "fmt func main() { ch := make(chan<- int, 2) ch <- 200 }Copy the code
To send a data to chan use “ch<-“. Here ch is chan int or chan <-int.
Channel Application Scenarios
- Data communication: Acts as a concurrent buffer or queue to solve producer-consumer problems. Multiple Goroutines can act concurrently as producers and consumers.
- Data passing: When one Goroutine hands data to another goroutine, ownership of the data is delegated.
- Signal notification: A Goroutine can send signals (closing, closed, data ready, etc.) to another goroutine or set of goroutines.
- Task choreography: A set of Goroutines can be executed concurrently or sequentially in a certain order. This is called choreography.
- Locking mechanism: Use channels to achieve mutual exclusion mechanism.
Channel implementation Principle
Channel data structure
Channel A type pipe through which messages can be sent and received between goroutines. It is a means of communication between Goroutines provided by Golang at the language level. As we all know, Go relies on a concurrent model called CSP(Communicating Sequential Processes), which is implemented through channels.
The channel structure
//path:src/runtime/chan.go
type hchan struct {
qcount uint // The number of remaining elements in the current queue column
dataqsiz uint // The length of the ring queue, i.e. the number of elements that can be stored
buf unsafe.Pointer // Ring queue column pointer
elemsize uint16 // The size of each element
closed uint32 // Indicates the shutdown status
elemtype *_type // Element type
sendx uint // Queue subscript, indicating the position x in the queue column when the element is written in
recvx uint // Queue subscript, indicating that the element is read from that position in the queue column
recvq waitq // The goroutine queue waiting to read messages
sendq waitq // The goroutine queue waiting to write messages
lock mutex // mutex, chan does not allow concurrent reads and writes
}
Copy the code
From the data structure, you can see that a channel consists of a queue, type information, and a Goroutine wait queue.
Channel implementation
Chan internally implements a buffer queue as a buffer, the length of which is specified when the chan is created.
The following diagram shows a channel that can cache six elements:
- Dataqsiz: The length of the pointing queue is 6, and 6 elements can be cached
- Buf: Memory pointing to a queue with two remaining elements
- Qcount: number of remaining elements in the current queue
- Sendx: indicates the position of the element to be written later
- Recvx: reads data from this location
Waiting queue
Read data from a channel. If the channel buffer is empty or there is no buffer, the current goroutine is blocked. Write data to a channel. If the channel buffer is full or has no buffer, the current goroutine will block.
A blocked goroutine is suspended in a channel’s wait queue:
- A goroutine blocked by a read is woken up by a Goroutine writing data to a channel
- A goroutine blocked by a write will be woken up by a Goroutine reading data from a channel
Here is a channel with no buffers and several goroutines blocking waiting for data:
Note that in general, at least one of recvq and sendq is empty. The only exception is when the same Goroutine uses a SELECT statement to write and read data to a channel.
Write data to channel
Flow chart:
The detailed process
- If the RECVQ queue is not empty, it indicates that the buffer has no data or buffer. In this case, a G is directly fetched from the RECVQ wait queue, and the data is written. Finally, the G is woken up to end the sending process.
- If the buffer has free space, the data is written to the buffer to end the sending process.
- If there is no free space in the buffer, the data is written to G, the current G is written to the sendq queue, and goes to sleep, waiting to be awakened by reading goroutine.
Read data from channel
The flow chart
The detailed process
- If the waiting sending queue Sendq is not empty and there is no buffer, G is directly taken out from the sendq queue, the data in G is read out, and finally G is woken up to end the reading process.
- If sendq is not empty, it indicates that the buffer is full, read data from the head of the buffer queue, take out G from sendq wait sending queue, write the data in G to the end of the buffer to end the reading process;
- If there is data in the buffer, the data is fetched from the buffer, ending the reading process.
Precautions for channel
- Panic occurs when writing data to a closed channel
- Closing a closed channel causes Panic
- Closing a channel with a value of nil causes Panic
Small talk
- After reading the article, I am not sure whether the CP rate of the channel channel has increased again
- I’m Maimo, welcome to talk to me
Original is not easy, feel that the article is written well small partners, a praise 👍 to encourage it ~
Welcome to join my official account [Maimocoding] pk big factory together
Welcome to Maimocoding