Channel Introduction to Golang
A channel is a link from one goroutine to another and this channel is synchronous because of blocking data processes. Channels can be defined as variables with the keyword chan. This variable has the task of sending and receiving data.
Here is an example of a channel implementation that has 3 new goroutines executed and each gouroutine processes data through the channel. The data will be received 3 times in the main goroutine (main).
1package main
2
3import (
4 "fmt"
5 "runtime"
6)
7
8func main() {
9 runtime.GOMAXPROCS(2)
10 var messages = make(chan string)
11 var sayHelloTo = func(who string) {
12 var data = fmt.Sprintf("hello %s", who)
13 messages <- data
14 }
15
16 go sayHelloTo("captain america")
17 go sayHelloTo("captain marvel")
18 go sayHelloTo("black panter")
19
20 var message1 = <-messages
21 fmt.Println(message1)
22
23 var message2 = <-messages
24 fmt.Println(message2)
25
26 var message3 = <-messages
27 fmt.Println(message3)
28}In the above code, the variable messages is declared to be of type channel string. The way to initialize the channel variable is by writing make with the chan keyword followed by the desired channel data type.
1var messages = make(chan string)closure sayHelloTo function that returns string data. The data is then sent through the messages channel. The <- sign if written to the left of the variable name then the process is in progress of sending data from the variable on the right through the channel on the left. In this context, the data variable is sent through the messages channel.1var sayHelloTo = func(who string) {
2 var data = fmt.Sprintf("hello %s", who)
3 messages <- data
4}sayHelloTo function is executed 3 times as different goroutines. Making these three processes run asynchronously or not waiting for each other.1go sayHelloTo("captain america")
2go sayHelloTo("captain marvel")
3go sayHelloTo("black panther")Of the three functions, the earliest goroutine is in charge of sending data and will be received by the variable message1. The <- sign if written to the right of the channel, indicates the process of receiving data from the channel on the right, to be stored in the variable on the left.
1var message1 = <-messages
2fmt.Println(message1)Since channel is blocking, it means that the statement var message1 = <-messages until after will not be executed until there is data sent through the channel.
The three goroutines will be received sequentially by message1, message2 and message3 and then displayed.
1➜ channel git:(main) ✗ go run main.go
2hello black panter
3hello captain marvel
4hello captain america
5➜ channel git:(main) ✗ go run main.go
6hello black panter
7hello captain marvel
8hello captain america
9➜ channel git:(main) ✗ go run main.go
10hello black panter
11hello captain marvel
12hello captain americasayHelloTo is not always sequential, even though the data received is sequential. This is because it sends data from 3 different goroutines, which we don’t know which one was executed first. The earlier the goroutine is executed, the earlier the data will be received.It has been mentioned above that this channel is blocking so there is no need to use the fmt.Scanln() command to do the blocking.
Channel Data Type Parameter
Channel variables can be passed to other functions as parameters. This is done by adding the chan keyword when declaring it.
Let’s continue with the practice. Set up the printMessage function with a channel parameter, then retrieve the data sent through the channel for display.
1func printMessage(what chan string) {
2 fmt.Println(<-what)
3}main function. 1func main() {
2 runtime.GOMAXPROCS(2)
3 var messages = make(chan string)
4
5 for _, each := range []string{"america", "marvel", "panther"} {
6 go func(who string) {
7 var data = fmt.Sprintf("hello %s", who)
8 messages <- data
9 }(each)
10 }
11
12 for i := 0; i < 3; i++ {
13 printMessage(messages)
14 }
15}what parameter in the printMessage function is of type channel string. Data passing operations can be performed on that variable, and will also affect the messages variable in the main function.Passing data of type channel via parameter is implicitly pass by reference, what is passed is the pointer. The output of the above program is the same as the previous program.
1➜ channel-parameter git:(main) ✗ go run main.go
2hello america
3hello marvel
4hello panther
5➜ channel-parameter git:(main) ✗ go run main.go
6hello panther
7hello marvel
8hello americaLet’s take a look at how the above program works.
Iteration of Array Data on Initialization
Newly initialized array data can be directly iterated, it’s easy by writing it directly after the range keyword.
1for _, each := range []string{"america", "marvel", "panther"} {
2 // ...
3}Buffered Channel
Channels are by default un-buffered, not buffered in memory. When a goroutine sends data through a channel, there must be another goroutine in charge of receiving data from the same channel with a blocking handover process. That is, the lines of code in the sending and receiving part of the data will not be processed until the handover process is complete.
The Buffered channel is slightly different. In this type of channel, the number of buffers is determined. This number will determine when we can send data. As long as the amount of data sent does not exceed the buffer amount, the transmission will run asynchronous (no blocking).
When the amount of data sent has exceeded the buffer limit, then sending data can only be done when one of the data has been taken from the channel, so there is an empty channel slot. The reception process itself is blocking.
Here is an example of the application of Buffered Channel by making a program that sending data via buffered channel is asynchronous as long as the amount of data being buffered by the channel does not exceed its buffer capacity.
1package main
2
3import (
4 "fmt"
5 "runtime"
6)
7
8func main() {
9 runtime.GOMAXPROCS(2)
10 messages := make(chan int, 2)
11 go func() {
12 for {
13 i := <-messages
14 fmt.Println("receive data", i)
15 }
16 }()
17 for i := 0; i < 5; i++ {
18 fmt.Println("send data", i)
19 messages <- i
20 }
21}make function represents the number of buffers. Now, we need to note that the value of buffered channel starts from 0. So, when the value is 2, it means that the maximum number of buffers is 3, namely 0, 1, 2.In the example above, there is a goroutine that contains the process of receiving data from the message channel which will then be displayed.
After the data receiving goroutine is executed, the data will be sent through the for loop. In the above program we will send a total of 5 data through the message channel sequentially.
1➜ channel-buffered git:(main) ✗ go run main.go
2send data 0
3send data 1
4send data 2
5send data 3
6receive data 0
7receive data 1
8receive data 2
9receive data 3
10send data 4As a result of the above output, we can see that the 4th transmission is followed by the reception, and both processes are blocking. Why is that? Because when sending data to 0, 1, 2 and 3 will run asynchronously, this is because the channel we specify has a buffer value of 3 (starting from 0).
The next transmissions (4th and 5th) will only occur if there is one of the 4 data that has previously been sent and has been received (blocking data handover). After the channel slot is empty, the 4th and 5th data will be handed over again with aysnchronous.