Here you are in touch with the most core and important knowledge! Serious study you are great!

This section source location github.com/golang-mini…

What is the channel

Go is a programming language that supports concurrency at the language level, and it has a very specific design philosophy that you don’t communicate through shared memory, you communicate through shared memory, which sounds a little convoluted.

In traditional languages, global variables are used concurrently to share data between different threads by using shared memory to communicate. Go, on the other hand, makes a tunnel between coroutines and transmits data (send and receive) through this tunnel.

For example, we must have had a lot of contact with queues, which are characterized by first-in, first-out, multi-production and multi-consumption. This queue/tunnel is a channel.

A channel is something that communicates between goroutines. Goroutines send and receive messages. Essentially, you’re doing memory sharing between goroutines.

So let’s see what works.

Declaration and initialization

Channels are type dependent, that is, a channel can only pass a value of one type, which needs to be specified when a channel is declared.

The general declaration form of channel:

var chanName chantypeCopy the code

Unlike normal variable declarations, the channel keyword precedes the type, which specifies the type of elements that the channel can pass. Example:

var a chan int // Declare a channel that passes elements of type int
var b chan float64
var c chan string
Copy the code

A channel is a reference type with an initial value of nil. Channels with a value of nil are permanently blocked for receive and send operations, regardless of their type.

So it must be initialized manually by make. Example:

a := make(chan int) // Initialize a channel of type int named a
b := make(chan float64)
c := make(chan string)
Copy the code

Since it’s a queue, it has a size, which it doesn’t specify, and is considered unbuffered (note that size is 0, not 1), which means it has to be received by some other Goroutine, otherwise it will block there. Declare buffered, specify the size can be ok.

a := make(chan int.100)
Copy the code

How to use

Let’s take a closer look at what happens to unbuffered channels and familiarize ourselves with their usage, for example:

func pendingForever(a) {
a := make(chan int)
a <- 1   // Write data to channel
z := <-a // Read data from channel
fmt.Println(z)
}
Copy the code
  • Look at the three lines above, line 2channelLine 3 writes data fromchannelRead the data in
  • But this is in the same method and does not use the Go keyword, which means they are in the same coroutine

We said that a channel is used to communicate with different Goroutines, so it cannot be sent and received in the same coroutine, which cannot achieve the effect of tunnel communication at all. So the above code will deadlock:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chansend]: main.main() ... /4.concurrent/channel.go:7 +0x59
Copy the code

The reason for the deadlock is that there is no other coroutine to receive the data, and the tunnel is unbuffered, so it simply blocks at the sender forever.

It is easy to solve the problem. You can put them in different Goroutines.

func normal(a) {
chanInt := make(chan int)
go func(a) {
chanInt <- 1
}()

res := <-chanInt
fmt.Println(res)
}
Copy the code

Output 1. Buffered channel When no data is sent, the receiver blocks until new data is sent.

In the above code, one sends and one receives, whereas in practice data is often sent continuously. Take a look at this code:

func standard(a) {
chanInt := make(chan int)
go func(a) {
defer close(chanInt)
var produceData = []int{1.2.3}
for _, v := range produceData {
chanInt <- v
}
}()
for v := range chanInt {
fmt.Println(v)
}
}
Copy the code

The output

1
2
3
Copy the code
  • The parent coroutine receives the data in a loop.
  • range chanCan continuously receive data until the channel is closed. If the channel is closed, it will block forever and cannot be compiled.
  • The channel must be closed at the sender because the receiver cannot predict whether any data is still unreceived. To the closedchannelSending datapanic.
  • It is recommended to usedeferTo close the channel, to prevent abnormal procedures not normally closed.

So far we have completed a simple producer-consumer model.

The closing of the channel

Close a channel using the close() function built into the Go language, and again it is recommended to close it using defer, as shown in this example:

defer close(ch)
Copy the code

If a channel is closed, how do I check whether the channel is closed successfully? We can simply read a channel with multiple return values, as in:

x, ok := <-ch
Copy the code

If the value is false, the channel is closed. If the value is false, the channel is closed. If the value is false, the channel is not closed.

func main(a) {
var chanInt chan int = make(chan int.10)
go func(a) {
defer fmt.Println("chanInt is closed")
defer close(chanInt)
chanInt <- 1
}()
res := <-chanInt
fmt.Println(res)
}
Copy the code

The output

chanInt is closed
1
Copy the code
  • As stated above, a buffered channel does not need to block waiting for receiving as much as the buffer size allows
  • The sender actively closes the channel after sending
  • Although the channel has been closed, the receiver can still receive the data.

PS1: A channel can be closed only once. If the channel is closed repeatedly, panic will occur.

PS2: If you pass nil, such as close(nil) panic.

Multisend, multireceive, and unidirectional channels

Let’s combine the previous knowledge to practice!

Function: to achieve a multi – send, multi – receive example.

func send(c chan<- int, wg *sync.WaitGroup) {
c <- rand.Int()
wg.Done()
}
Copy the code
  • The sender randomly generates numbers and declares a one-way channel for sending only
  • usesync.WaitGroupDo wait (forget the previous section ha!)
func received(c <-chan int, wg *sync.WaitGroup) {
	for gotData := range c {
		fmt.Println(gotData)
	}
	wg.Done()
}
Copy the code
  • Use by receiverrangeTo receive the numbers and print them
func main(a) {
	chanInt := make(chan int.10)
	done := make(chan struct{})
    defer close(done)
	go func(a) {
		defer close(chanInt)
        / / send} ()go func(a){.../ / receive
		done <- struct{}{}
	}()
	<-done
}
Copy the code
  • Two channels are used, one channelchanIntData transfer. AnotherdoneEnd the main coroutine when control is complete
  • The sending end is responsible for data production and closes the channel after production
  • The receiver is responsible for notifying the main coroutine after receiving

The sender

go func(a) {
    var wg sync.WaitGroup
    defer close(chanInt)
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go send(chanInt, &wg)
    }
    wg.Wait()
}()
Copy the code

Start 5 coroutines in a row, use WG to do coroutine waiting, send and then finish so that defer can close chanInt

The receiving end

go func(a) {
    var wg sync.WaitGroup
    for i := 0; i < 8; i++ {
        wg.Add(1)
        go received(chanInt, &wg)
    }
    wg.Wait()
    done <- struct{} {}} ()Copy the code

Start multiple receivers continuously, exit when the channel is closed, and finally notify Done

Output 5 random numbers, the program normally closed.

5577006791947779410
8674665223082153551
4037200794235010051
6129484611666145821
3916589616287113937
Copy the code

One-way channels limit how functions can be used. They can be used in scenarios where the loop is time-consuming and a data is processed and sent immediately, minimizing memory usage.

summary

This section briefly introduces the Channel in go language. Go language advocates not to communicate through shared memory, but to share memory through communication. Communication between different Goroutines can be completed through channel.

We learned:

  • channelYes Reference type The default value isnil, need to manuallymake.
  • Channels must be in multiplegoroutineThe use of
  • There are buffered and unbuffered channels and when they block.
  • You can userangeTo do the loop reception, channel closing will automatically stop.
  • Only and must be used on the sending sidedeferClose the channel.
  • Formal use general multi – send multi – receive, and usedoneSignal notification mode for notification.

At work, the use of channels is more complicated, so stay tuned for select in the next section!