Go Channels

Go Channels

In this tutorial, we are going to discuss Go channels and how Goroutines communicate using channels. A channel is a medium through which a Goroutine communicates with another Goroutine, and this communication is lock-free.

In other words, a channel is a technique that allows to let one Goroutine send data to another Goroutine.

By default, channel is bidirectional, which means the Goroutines can send or receive data through the same channel, as shown in the below image.

Go Channels
Declaring Channels

In the Go language, a channel is created using the chan keyword, and it can only transfer data of the same type. Different types of data are not allowed to transport from the same channel.

var ChannelName chan Type

You can also create a channel using the make() function using a shorthand declaration.

channelName:= make(chan Type)

Lets write some code that declares a channel.

package main

import "fmt"

func main() {
    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)
    }
}

Output

Channel a is nil, going to define it 
Type of a is chan int

Run in playground

As usual the short hand declaration is also a valid and concise way to define a channel.

a := make(chan int) 
Send and Receive Data From a Channel

In the Go language, the channel works with two principal operations: sending and receiving, both operations collectively known as communication. The syntax to send and receive data from a channel are given below,

data := <- a // read from channel a   
a <- data // write to channel a 

The direction of the arrow with respect to the channel specifies whether the data is sent or received.

In the first line, the arrow points outwards from a, and hence we are reading from channel a and storing the value to the variable data.

The second line, the arrow, points towards a, so we are writing to channel a.

In the channel, the send and receive operation block until another side is not ready by default. It allows Goroutine to synchronize with each other without explicit locks or condition variables.

Send operation

The send operation is used to send data from one Goroutine to another Goroutine with the help of a channel.

Values like int, float64, and bool can be safe and easy to send through a channel because they are copied, so there is no risk of accidental concurrent access of the same value.

Similarly, strings are also safe to transfer because they are immutable. But sending pointers or references like a slice, map, etc., through a channel is not secure because the value of pointers or references may change by sending Goroutine or by the receiving Goroutine simultaneously, and the result is unpredictable.

So, when you use pointers or references in the channel, you must ensure that they can only access by one Goroutine at a time.

Mychannel <- element

The above statement indicates that the data(element) send to the channel (Mychannel) with the help of a <- operator.

Receive Operation

The receive operation is used to receive the data sent by the send operator.

element := <-Mychannel

The above statement indicates that the element receives data from the channel (Mychannel).

If the result of the received statement is not going to use is also a valid statement. You can also write a receive statement as:

<-Mychannel
package main

import "fmt"

func addNumber(ch chan int) {
	fmt.Println(200 + <-ch)
}
func main() {
	fmt.Println("Start Main method")
	// Creating a channel
	ch := make(chan int)

	go addNumber(ch)
	ch <- 100
	fmt.Println("End Main method")
}
Start Main method 
300 
End Main method

Run in playground

Closing a Channel

You can also close a channel with the help of close() function. This is an in-built function and sets a flag indicating that no more value will be sent to this channel.

You can also close the channel using for range loop. Here, the receiver Goroutine can check the channel is open or close with the help of the given syntax.

element, ok:= <- Mychannel

Here, if the value of ok is true which means the channel is open so, read operations can be performed. And if the value of ok is false which means the channel is closed so, read operations are not going to perform.

package main

import "fmt"

func myfun(mychnl chan string) {
	for v := 0; v < 5; v++ {
		mychnl <- "Waytoeasylearn"
	}
	close(mychnl)
}

func main() {
	c := make(chan string)

	// calling Goroutine
	go myfun(c)

	for {
		data, ok := <-c
		if ok == false {
			fmt.Println("Channel is Closed")
			break
		}
		fmt.Println("Channel is Open, Channel Data is ", data)
	}
}
Channel is Open, Channel Data is  Waytoeasylearn 
Channel is Open, Channel Data is  Waytoeasylearn 
Channel is Open, Channel Data is  Waytoeasylearn 
Channel is Open, Channel Data is  Waytoeasylearn 
Channel is Open, Channel Data is  Waytoeasylearn 
Channel is Closed

Run in playground

Note

Zero Value Channel: The zero value of the channel is nil.

Blocking Send and Receive

In the channel when the data sent to a channel the control is blocked in that send statement until other Goroutine reads from that channel. Similarly, when a channel receives data from the Goroutine the read statement block until another Goroutine statement.

For loop in Channel

A for loop can iterate over the sequential values sent on the channel until it closed.

package main

import "fmt"

func main() {
	mychnl := make(chan string)
	go func() {
		mychnl <- "Ashok Kumar"
		mychnl <- "Sai"
		mychnl <- "Rama"
		mychnl <- "Seetha"
		close(mychnl)
	}()
	for res := range mychnl {
		fmt.Println(res)
	}
}

Output

Ashok Kumar 
Sai 
Rama 
Seetha

Run in playground

Length and Capacity of the Channel

In the channel, you can find the length of the channel using the len() function. Here, the length indicates the number of values queued in the channel buffer.

In the channel, you can find the capacity of the channel using the cap() function. Here, the capacity indicates the size of the buffer.

package main

import "fmt"

func main() {
     mychnl := make(chan string, 10)

     mychnl <- "Ashok Kumar"
     mychnl <- "Sai" 
     mychnl <- "Rama"
     mychnl <- "Seetha"

     fmt.Println("Length of the channel is: ", len(mychnl))
     fmt.Println("Capacity of the channel is: ", cap(mychnl)) 
}

Output

Length of the channel is:  4 
Capacity of the channel is:  10

Run in playground

Select and case statement in Channel

In the go language, a select statement is just like a switch statement without any input parameter. This select statement is used in the channel to perform a single operation out of multiple operations provided by the case block.

Deadlock

One crucial factor to consider while using channels is Deadlock. If a Goroutine is sending data on a channel, it is expected that some other Goroutine will receive the data. If this does not happen, then the program will panic at runtime with Deadlock.

Similarly, if a Goroutine is waiting to receive data from a channel, then some other Goroutine is expected to write data on that channel, else the program will panic.

Go Channels
Scroll to top