In Golang, channel provides a mechanism to communicate between different goroutines. This tutorial gives you some highlights when working with channel

Create a new channel

You have to create a new channel before using it with the make function (same as with slice and map)

c := make(chan Type)

Channels behave like pointers

The created channel acts as a reference to the underlying data structure, so it's unnecessary to use pointer to channel

Send and receive messages with channel

You can use channel to send and receive messages of the same type between goroutines

Lets take a look at the following example


Output

hello, channel!

In the above program

  • We created a string channel before using with make
c := make(chan string)
  • You can use <- operator to send and receive messages from channel. We sent "hello, channel!" to channel c inside a new goroutine
c <- "hello, channel!"
  • And received message from c in the main goroutine
s := <- c

Channel types

There're 3 types of channel based on the channel direction operator <-

  • chan T is a bidirectional channel, can send and received messages of type T

  • chan <- string is a receive channel, can only receive messages of type string

  • chan -> int is a send channel, can only send messages of type int

Unbuffered vs buffered channel

Channels are unbuffered and block by default

When you allocate a new channel with the make function

c := make(chan string)  

The created channel c is unbuffered and block / synchronous, sends and receives block until the other side is ready

package main

import (  
    "fmt"
)

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

    uc <- "hello, unbuffered channel!"
    s := <- uc

    fmt.Println(s)
}

Output

fatal error: all goroutines are asleep - deadlock!

The program throws a panic as uc <- "hello, channel!" blocks the main goroutine because of no receives is ready

Buffered channels only block when full

You can provide an optional integer argument to indicate the buffered size when create a new channel with make

c := make(chan string, 2)

Sends to a buffered channel block when the buffer is full

package main

import (  
    "fmt"
)

func main() {  
    bc := make(chan string, 2)

    bc <- "hello, buffered channel!"
    bc <- "may i send more?"
    s1 := <- bc
    s2 := <- bc

    fmt.Println(s1)
    fmt.Println(s2)
}

Output

hello, buffered channel!  
may i send more?

Try to modify the above program to send more than 2 messages to the buffered channel then comparing the outputs

Close a channel

You can close a bidirectional or send-only channel c by using the built-in function close(c)

Only the sender should close a channel to indicate that no more values will be sent

You can still receive messages from a closed channel. After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel's type without blocking.

Usually, you don't have to close a channel except when you want to use it as an ending signal