In this tutorial, you will study channel concepts in Golang. Channel in Go programming language is related to the Goroutine in Golang which is discussed in our previous tutorial. You can refer to the Go routine then only you will understand the channels we are going to discuss in this tutorial.
In Golang a channel is a mechanism that allows sharing of data between Goroutine. A Goroutine allows concurrent execution of activities in a Go program which shares data or resources between Goroutines. Golang supports 1000s of Goroutines which share data or resources during their execution to achieve concurrency. (We have already discussed this in the Goroutine tutorial)
Goroutine A writes some data into the channel and Goroutine B reads data from the same channel. Thus channel (path) helps in data transferring or communication between Goroutines.
To better understand the concept of the channel we will just see briefly what is happening in Goroutine.
Let us understand with the same example we discussed in the Goroutine tutorial.
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Go program starts")
msg :="learn eTutorial"
go func(){ //anonymous function
fmt.Println(msg)
}()
msg = "hello users"
fmt.Println("Go program ends")
time.Sleep(100 * time.Millisecond)
}
The above-given program consists of two blocks of codes.
The two segments of codes execute independently of each other. As a result of independent execution of main () and hello () function Goroutine there arises the following issues:
A new keyword
called chan
declares a channel in Golang.
Syntax for declaration
var <variable name> chan <data type>
Here c is a variable of channel type declared with chan keyword of integer (int) data type
Note: Default value of channel when declared is nil same as we discussed in Goroutine
In Golang channels are constructs that allow effective communication between definite Goroutines in our Go program. The go channel concept has been around since the mid of 1970s
Let us see how to instantiate a channel & use it for communication across different Goroutines.
Step by step we will understand.
msgs
.make
“ followed by chan
keyword with its type
is declared. In the given program data type is an integer.send operator ( <- )
is defined to send value to this channel.go SendMsg(msgs)
within the main function.variable msg
. This is displayed in the output. This is a simple example of an integer channel Note: A channel is created using the make function, which specifies the chan keyword and a channel's element type.
When we create a channel without specifying the size of the channel it is known as an unbuffered channel. You will learn the same in the next section.
package main
import (
"fmt"
)
func SendMsg(c chan int){
c <-0
}
func main() {
fmt.Println("Learn eTutorial")
msgs := make(chan int) // instantiating a channel
defer close(msgs)
go SendMsg(msgs)
msg := <-msgs
fmt.Println(msg)
}
Output:
Learn eTutorial 0
There are two types of channels based on the transfer of data between Goroutines. They are
Let us consider the same program described above but the only difference is we create a channel of string data type.
In order to better understand the same concept, some modifications are done to the above code which is given below
package main
import (
"fmt"
"time"
)
func SendMsg(c chan string){
fmt.Println("start Goroutine execution")
time.Sleep(1 * time.Second)
c <-"hello world"
fmt.Println("finished Goroutine execution")
}
func main() {
fmt.Println("Learn eTutorial")
msgs := make(chan string) //created channel of string type.
defer close(msgs)
go SendMsg(msgs)
go SendMsg(msgs)
msg := <-msgs
fmt.Println(msg)
}
Output:
Learn eTutorial start Goroutine execution start Goroutine execution finished Goroutine execution hello world
func SendMsg(c chan string){
fmt.Println("start Goroutine execution")
time.Sleep(1 * time.Second)
c <-"hello world"
fmt.Println("finished Goroutine execution")
}
In this part of the code two print statements ("start Goroutine execution
") & ("finished Goroutine execution
") are defined. Sleep statement to allow Goroutine to sleep for some seconds. After the send operation to the channel i.e. c <-"hello world"
prints that Goroutine execution is finished.
Also duplicated the go SendMsg(msgs)
statement. There are two Goroutines that are trying to send the same channel.
There is only one receive statement msg := <-msgs
to the channel in the above program so only one of the Goroutine is expected to finish its execution. Only one Goroutine will execute and print the last print statement.
The second Goroutine will not finish its execution gets blocked within the main function. The program will terminate before the second Goroutine finishes its execution
To get rid of blocking behavior we specify the size of the channel. When a channel is created with specifying the size of the channel it is known as a buffered channel.
Syntax
Buffered channel: = make (chan datatype, size)
Example
msgs: = make (chan string, 2)
By changing this to a buffered channel, send operation, c <-"hello world" only blocks within Goroutines.
import (
"fmt"
"time"
)
func SendMsg(c chan string){
fmt.Println("start Goroutine execution")
time.Sleep(1 * time.Second)
c <-"hello world"
fmt.Println("finished Goroutine execution")
}
func main() {
fmt.Println("Learn eTutorial")
msgs := make(chan string,2) //created channel of string type.
defer close(msgs)
go SendMsg(msgs)
go SendMsg(msgs)
msg := <-msgs
fmt.Println(msg)
time.Sleep(1 * time.Second)
}
Output:
Learn eTutorial start Goroutine execution start Goroutine execution finished Goroutine execution hello world finished Goroutine execution
A sleep() statement is provided at last because there is no guarantee that the second Goroutine will finish before the first Goroutine exits.
From the output, it is clear that the first and second Goroutine starts execution & similarly both terminate.
Buffered channels are used in cases where we don’t want the channel statement to block if there are no available receivers. Adding a buffer allows us to wait for some of the receivers to get freed without blocking the sending code.
The basic operations provided in the channel are read and write. The read and write to a channel is represented using the left arrow <-
Note: The same operator denotes send/receive operations in channels.
Here the channel sends and receives data. Let the blue circle be a string “hello world” message.
Send operation: This indicates that a message “hello world”(the data) is sent to the channel created as c. (already discussed making channel refer above sections).
c <-"hello world"
Receive operation: In this operation, the message fed to the channel by Goroutine A is received by another Goroutine B by creating a new variable called msg.
msg := <-c
package main
import (
"fmt"
"time"
)
func myfunc(c chan string) {
fmt.Println("start Goroutine execution")
time.Sleep(1 * time.Second)
c <-"hello world" //send msg hello world to channel c
fmt.Println("finished Goroutine execution")
}
func main() {
fmt.Println("start Main method")
// Creating a channel
c := make(chan string)
go myfunc(c)
msg := <-c //receive msg helloworld from channel c
fmt.Println(msg)
fmt.Println("End Main method")
}
Output:
start Main method start Goroutine execution finished Goroutine execution hello world End Main method