DEV Community

Cover image for Channels in Go
Saptarshi Das
Saptarshi Das

Posted on

Channels in Go

Channels are a typed, thread-safe queue and it allows different go-routines to communicate with each other. Channels can be created by using make keyword.

For Example:-

<Channel Name> := make(chan <Channel Type>)
Enter fullscreen mode Exit fullscreen mode

We can send and receive data in channels in Go lang. In Go lang this <- operator is called channel operator. Data flows in the direction of the arrow. This operation will block until another go-routine is ready to receive the value. Example:- If we want to send data to a channel we will be using the below code snippet:

<Channel name> <- <Input Value>
Enter fullscreen mode Exit fullscreen mode

if we want to receive data from the channel we will be using the below code snippet:

<Variable> := <- <Channel Name>
Enter fullscreen mode Exit fullscreen mode

This reads and removes a value from the channel and saves it into the variable. This operation will block until there is a value in the channel to be read.

Deadlock and Blocking

A deadlock is when a group of go-routines are all blocking so none of them can continue. This is a common bug that we need to watch out for in concurrent programming.

We can block and wait until something is sent on a channel using the code snippet:

<-<Channel Name>
Enter fullscreen mode Exit fullscreen mode

This will block until it pops a single item off the channel, then continue, discarding the item.

Buffered Channels

Channels can optionally be buffered. We can provide a buffer length as the second argument to make() to create a buffered channel:

<Channel Name> := make(chan, <Buffer Length>)
Enter fullscreen mode Exit fullscreen mode

Sending on a buffered channel only blocks when the buffer is full. Receiving blocks only when the buffer is empty.

Closing Channels in Go
Channels can be explicitly closed by a sender:

<Channel Name> := make(chan <Channel Type>)

// do some stuff with the channel

close(<Channel Name>)
Enter fullscreen mode Exit fullscreen mode

Now, we can check if the channel is closed or not by using "ok" after the variable for example:

<Variable>,ok := <-<Channel Name>
Enter fullscreen mode Exit fullscreen mode

receivers can check the ok value when receiving from a channel to test if a channel was closed.

If ok is false if the channel is empty and closed.

Note

_Sending on a closed channel will cause a panic. A panic on the main go-routine will cause the entire program to crash, and a panic in any other go-routine will cause that go-routine to crash.

Closing isn't necessary. There's nothing wrong with leaving channels open, they'll still be garbage collected if they're unused. You should close channels to indicate explicitly to a receiver that nothing else is going to come across._

Range in Channels

Channels can be ranged over.

for <item> := range <Channel Name> {
    // item is the next value received from the channel
}
Enter fullscreen mode Exit fullscreen mode

This example will receive values over the channel (blocking at each iteration if nothing new is there) and will exit only when the channel is closed.

Select Statement

Now if we have a single go-routine listening to multiple channels and want to process data in the order it comes through each channel.

A select statement is used to listen to multiple channels at the same time. It is similar to a switch statement but for channels.

select {
  case i, ok := <- <Channel Name>:
    fmt.Println(i)
  case s, ok := <- <Channel Name>:
    fmt.Println(s)
}
Enter fullscreen mode Exit fullscreen mode

The first channel with a value ready to be received will fire and its body will execute. If multiple channels are ready at the same time one is chosen randomly. The ok variable in the example above refers to whether or not the channel has been closed by the sender yet.

Default Case

The default case in a select statement executes immediately if no other channel has a value ready. A default case stops the select statement from blocking.

select {
  case v := <- <Channel Name>:
    // use v
  default:
    // receiving from channel would block
    // so do something else
}
Enter fullscreen mode Exit fullscreen mode

There are two type of channels are - Read-Only Channels and Write-Only Channels.

Read-Only Channel - A channel can be a read-only by casting it from a chan to a <-chan type. For example:

func main(){
  <Channel Name> := make(chan <Type>)
  readCh(<Channel Name>)
}

func readCh(<Channel Name> <-chan <Type>) {
  // ch can only be read from
  // in this function
}
Enter fullscreen mode Exit fullscreen mode

Write-Only Channel - The write-only channels can be created by changing the position of the arrow.

func writeCh(<Channel Name> chan<-<Type>) {
  // ch can only be written to
  // in this function
}
Enter fullscreen mode Exit fullscreen mode

You can also give a visit to the article Dave Cheney's awesome article, here you can learn about the extreme cases of Channels.

Top comments (1)

Collapse
 
dyfet profile image
David Sugar

Channels are a big part of what does make go special for applications. I wish at times it were possible to query a channel (maybe ?chan) for pending data rather than thru select, and to flush a buffered channel without closing.