Channel in Golang


January 17, 2022, Learn eTutorial
1550

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.

What are channels in Golang?

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)

  •    A channel in Golang is a medium through which Goroutines can communicate with each other.
  •   In other words, a channel is a pipe that allows Goroutine to perform certain operations like reading/writing, etc.
  •  Through the channels, Goroutine transfers their data or resources, where one Goroutine writes data into the channel and another Goroutine will read from the same channel.  
GO : Channels

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.

What is the need for the channel in Golang?

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.

  1.     main ( ) function
  2.     hello ( )
GO : Channels

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:

  1.  Sometimes the program ends/terminates without executing hello () Goroutine so we solve this by a sleep () statement.
  2.  Another issue is you cannot access the result of the hello ( ) function within the main () function. 

How to declare a channel in Golang?

A new keyword called chan declares a channel in Golang.
Syntax for declaration  


var <variable name> chan <data type> 

GO : Channels

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

How to add channels between Goroutines?

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.

  1.  The first step is to open the main () function & instantiate a new channel known as msgs.
  2.  In order to instantiate or create a channel, we use the keyword “ make “ followed by chan keyword with its type is declared. In the given program data type is an integer.
  3.  Next in the program, a defer keyword is called to close the channel.
  4.  The act of closing stops the Goroutines from sending or receiving to that channel.
  5.  In order to interact with channels in go, we use to send or receive operators.
  6.  A SendMsg Goroutine function is created which takes to a channel & send operator ( <- ) is defined to send value to this channel.
  7. This is sent to the channel by calling  go SendMsg(msgs) within the main function.
  8.  Then will receive the value from SendMsg in order to receive, receive operator is used which blocks until a value is sent to that channel.
  9.  When we run the code we can see that the value is successfully sent from the Goroutine function to the 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

What are the types of channels in Golang?

There are two types of channels based on the transfer of data between Goroutines. They are

  1.  Unbuffered channels
  2.  Buffered channels
     
GO : Channels

What are unbuffered channels in Golang?

Let us consider the same program described above but the only difference is we create a channel of string data type.

  •  When a channel is created without specifying the size of the channel technically it is known as an unbuffered channel.
  •  In an unbuffered channel, communication succeeds between Goroutines only when both sender and receiver are ready.
  •  In a traditional channel in any go program, there may be some unexpected behavior.
  •  In an unbuffered channel whenever a Goroutine says A sends a value to a channel that Goroutine A will be blocked until the value is received from that channel.  

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  

What are buffered channels in Golang?

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.  

What are channel properties in Golang?

  •   Channels Provide provision to block and unblock Goroutines.
  •   FIFO semantics are provided by the channel
  •   Secure and safe transfer of data between Goroutines.
  •   Easy to create and use

What are channel operations in a Golang?

The basic operations provided in the channel are read and write. The read and write to a channel is represented using the left arrow <-

GO : Channels

Note: The same operator denotes send/receive operations in channels.

  •  The communication between Goroutines works based on these two operations, send and receive respectively.
  •  The <- channel operator indicated whether the data is received or sent by any Goroutine.
  •  By default send and receive operations are in a block state till the other side is ready to function.
  • This operation with a block state allows achieving synchronization of Goroutines with each other
  • No conditional variables or locks are used to synchronize just these two operations carry out synchronization successfully.
GO : 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
 

// Go program to illustrate send and receive operation


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