This article is translated
Original address: golangbot.com/channels/
In this tutorial, we will discuss channels and how Goroutines use channels to communicate.
What is the Channel
You can think of a channel as a conduit for communication using a Goroutine. Similar to the way water flows from one end of a pipe to the other, a Channel can be used to send data from one end and receive data from the other.
Declare the Channel
Each Channel has its own type, which represents the type of data that the Channel can transmit. Other data types cannot transfer data using a Channel different from their Channel data type.
In chan T, where T represents the type of this Channel.
The zero value of a Channel is nil. A Channel with a value of nil cannot be used, so a Channel is defined using make like Map and Slice.
Let’s have some code to declare a Channel:
package main
import "fmt"
func main(a) {
var a chan int
if a == nil {
fmt.Println("channel a is nil, going to define it")
a = make(chan int)
fmt.Printf("Type of a is %T", a)
}
}
Copy the code
We declare a Channel A with a value of nil. Therefore, the statement in the if condition is executed and a Channel is defined using make. A is a Channel of type int.
The program output:
channel a is nil, going to define it
Type of a is chan int
Copy the code
In general, short declarations are also an efficient and concise way to define channels.
a := make(chan int)
Copy the code
The above code also defines a Channel A.
Channel Sends and receives data
The syntax for sending and receiving data from a channel is given below:
data := <- a // read from channel a
a <- data // write to channel a
Copy the code
Does the direction of the arrow relative to the channel specify whether to send or receive data?
In the first line, the arrow points outward from a, so we are reading from channel A and storing values into variable data.
In the second line, the arrow points to A, so we are writing channel A.
By default, the send and receive channels are blocked
By default, the send and receive channels are blocked. What does that mean? When data is sent to a channel, the control is blocked in the SEND statement until another Goroutine reads from that channel. Similarly, when reading data from a channel, reading is blocked until some Goroutine writes data to that channel.
This property of the channel helps Goroutine communicate effectively without using explicit locks or condition variables that are common in other programming languages.
Sample code for Channel
Let’s write a program to understand how Goroutines use channels to communicate.
In fact, we’ll use the channel here to rewrite the program we wrote when we learned Goroutines.
To quote from last time:
package main
import (
"fmt"
"time"
)
func hello(a) {
fmt.Println("Hello world goroutine")}func main(a) {
go hello()
time.Sleep(1 * time.Second)
fmt.Println("main function")}Copy the code
This is the code from the previous article. We use sleep to put the main coroutine to sleep so that the Go Hello () coroutine has time to execute. If you don’t understand, take a look at the previous article on Goroutines.
We use Channel based on the above program.
package main
import (
"fmt"
)
func hello(done chan bool) {
fmt.Println("Hello world goroutine")
done <- true
}
func main(a) {
done := make(chan bool)
go hello(done)
<-done
fmt.Println("main function")}Copy the code
In the code above, we create a channel of type bool called done and pass it as an argument to Go Hello (). It then receives data from the DONE channel on the next line, and blocks until another Goroutine writes to the channel. Thus, this eliminates the need for time.sleep, which was already present in the original program to prevent the main Goroutine from exiting.
The code <-done receives data from the completed channel, but does not use it or store it in any variables. It’s perfectly legal.
Our main Goroutine is now blocked waiting for data on the done channel. Hello Goroutine receives this channel as an argument, prints Hello World Goroutine, and writes true to the done channel. When the write is complete, the main Goroutine receives data from the completed channel, unblocks it, and then prints the main Function text.
Program output:
Hello world goroutine
main function
Copy the code
Let’s modify this program to better understand this blocking concept by introducing sleep in the Hello Goroutine.
package main
import (
"fmt"
"time"
)
func hello(done chan bool) {
fmt.Println("hello go routine is going to sleep")
time.Sleep(4 * time.Second)
fmt.Println("hello go routine awake and going to write to done")
done <- true
}
func main(a) {
done := make(chan bool)
fmt.Println("Main going to call hello go goroutine")
go hello(done)
<-done
fmt.Println("Main received data")}Copy the code
In the code above, we set sleep for 4 seconds in the Hello coroutine.
The program first prints Main Going to Call Hello Go Goroutine. And then the Hello coroutine will start executing and saying Hello Go routine is going to sleep. After output, the Hello coroutine will sleep for four seconds, during which time the main coroutine will block until it can read from done. After 4 seconds, the Hello Go routine awake and going to write to done message is displayed, followed by Main Received data message.