This is a creation in Article, where the information may have evolved or changed.
"Go language Combat" reading notes, not to be continued, welcome to sweep code attention flysnow_org to the public, the first time to see follow-up notes. If you feel helpful, share it with your friends and thank you for your support.
The last one we talked about atomic functions and mutexes can guarantee the read and write of shared data, but they are still a bit complex and affect performance, and for that, go provides us with a tool, which is the channel.
So in multiple goroutine concurrency, we can not only guarantee the secure access to the shared resource through the atomic function and the mutex, eliminate the state of competition, but also can send and accept the shared data in multiple goroutine by using the channel, and achieve the purpose of data synchronization.
Channel, he's kind of like a pipe set up between two routine, a goroutine can plug data into this pipe, and another can fetch data from this pipe, a bit like the queue we're talking about.
Declaring a channel is simple, we use the chan keyword, besides, we also specify the type of data that is sent and received in the channel, so that we know what type of data to send to the channel and what kind of data can be received from the channel.
Channel type and map these types, you can use the built-in make function declaration initialization, here we initialize a chan int type of channel, so we can only send to this channel int type of data, of course, receive can only be the int type of data.
We know that the channel is used to communicate between Goroutine, which has two operations to send and receive, and the operators of both operations are <- .
123 |
2 //Send value 2 to this channel //Read the value from the channel and assign the value of the read to the X variable //Read the value from the channel, and then ignore |
Take a look at examples and slowly understand the use of sending and receiving. Send Operation <- at the back of the channel, look at the direction of the arrow, indicating that the value 2 is sent to the channel ch ; the receive Operation <- is in front of the channel, and it is a unary operator, looking at the direction of the arrows, indicating that the data is read from the channel ch . The data read can be assigned to a variable, or it can be ignored.
Channel we can also use the built-in close function to close.
If a channel is closed, we will not be able to send data to this channel, if sent, will cause an painc exception. However, we can also receive the data in the channel, if there is no data in the channel, the data received is nil .
Just when we use make the function initialization, there is only one parameter, in fact, make there can be a second parameter to specify the size of the channel. By default, when there is no second parameter, the channel size is 0, and this channel is also made 无缓冲通道 .
123 |
ch:=make (chanint) ch:=make (chanint,0) ch:= make (chanint,2) |
Look at an example where the first and second initializations are equivalent. The third initialization creates a channel of size 2, which is called 有缓冲通道 .
Non-buffered channels
The unbuffered channel refers to the size of the channel is 0, that is, this type of channel is not able to save any value before receiving, it requires sending Goroutine and receive goroutine at the same time ready to complete the send and receive operations.
From the above unbuffered channel definition, the Send goroutine and receive gouroutine must be synchronous, and when prepared, the first action will block the wait until another corresponding operation is ready. This unbuffered channel is what we call it 同步通道 .
1234567891011121314 |
func Main ()make (chanint)gofunc() { var int 0 for 0 ten; i++ {sum + = I}ch <-sum} () fmt. Println (<-ch)} |
In the previous example, in order to demonstrate the goroutine, to prevent the program early termination, are used sync.WaitGroup to wait, this example is not necessary, we use synchronous channel to wait.
After calculating sum and the goroutine is not executed, the value is assigned to the ch channel before it fmt.Println(<-ch) waits, so the main main goroutine will not terminate, only after the calculation and the Goroutine is complete, and the operation sent to the ch channel is ready. , the <-ch calculated value is received and then printed out.
Pipeline
When we use bash, we have a pipeline operation that means that the output of the | previous operation, as input to the next operation, is connected to a series of processing operations.
1234 |
➜ ' D ' desktopdocumentsdownloads |
As the above example means, first use the ls command, the current directory of directories and files listed, as the input of the next grep command, and then through the grep command, matching we need to display the directory and file, here to match the D beginning of the file name or directory name.
In fact, we use the channel can also achieve the effect of the pipeline, we just need to put a channel output, as the next channel input.
12345678910111213141516 |
func main Span class= "params" > () {one: = make (chan span class= "keyword" >int ): = make (chan int ) go func () {one<-100 } () Go func () {v:=< -ONETWO<-V} () fmt. Println (<-two)} |
In this example we define two channels one and two then sequentially, send 100 to the channel one , then use another goroutine to receive the value from, then send to the one channel, and two eventually wait in the main goroutine to receive the print The two value in the channel, which is similar to the operation of a pipe, the output of the channel, one two as the input of the channel, similar to the relay.
A buffered channel.
There is a buffer channel, is actually a queue, the maximum capacity of this queue is when we use the make function to create a channel, the second parameter is specified.
This creates a buffered channel with a capacity of 3. For a buffered channel, sending an action to it is the insertion of an element into the tail of the queue, and the receive operation removes the element from the head of the queue and returns the element that was just deleted.
When the queue is full, the send operation is blocked, and when the queue is empty, the accept operation is blocked. There is a buffered channel that does not require the send and receive operations to be synchronized, instead the send and receive operations can be decoupled.
Want to know the capacity of the channel and how many elements are there? In fact and the map same, the use cap and len function can be.
capThe function returns the maximum capacity of the channel, and the len function returns a few elements in the channel now.
12345678 |
func mirroredquery() string {Responses: = Make(Chan string,3)Go func() {Responses <-request ("Asia.gopl.io") }()Go func() {Responses <-request ("Europe.gopl.io") }()Go func() {Responses <-request ("Americas.gopl.io") }()return<-responses//Return the quickest response} func request(hostname string) (response string) {/* ... */} |
This is a more meaningful example of the Go Language Bible, an example of which is to get a data on the server, but this data exists on three mirror sites, and the three mirrors are geographically dispersed, and our goal is to get the data as fast as possible.
So here, we define a channel with a capacity of 3 responses , and then simultaneously initiate 3 concurrent goroutine to the three mirrors to get the data, the obtained data is sent to the channel responses , and finally we use the return <-responses first data obtained by the return, That is, the data of the image that is returned the fastest.
Unidirectional Channel
Sometimes, we have some special scenarios, such as restricting a channel can only receive, but not send, sometimes limit a channel can only send, but not receive, this channel we call one-way channel.
Defining a one-way channel is also simple, just take it with you when you define it <- .
12 |
var send chan<-int//can only send var receive <-chan int//can only receive |
Note that the <- operator, in the following is only sent, the corresponding send operation, in front is only receive, corresponding receive operation.
One-way channels are used for functions or methods with more parameters, such as
12 |
funccounterchanint) {} |
Examples of such, can only be sent to prevent misoperation, using the receive operation, if the use of the receive operation, at compile time will be error.
Using a channel can easily share data between goroutine, and the next article will describe some examples to better understand concurrency.
"Go language Combat" reading notes, not to be continued, welcome to sweep code attention flysnow_org to the public, the first time to see follow-up notes. If you feel helpful, share it with your friends and thank you for your support.