Buffered Channels in Go

Buffered Channels in Go

In this tutorial, we are going to discuss buffered channels in Go language. All the channels we discussed in the previous tutorial were unbuffered. As discussed in the channels tutorial, sends and receives to an unbuffered channel are blocking.

Buffered Channels in Go

Channels can be defined as pipes using which Goroutines communicate. Like water flows from one end to another in a pipe, data can be sent from one end and received from other using channels.

By default, channels are unbuffered, which states that they will only accept sends (chan <-) if there is a corresponding receive (<- chan) that are ready to receive the sent value.

Buffered channels allow accepting a limited number of values without a corresponding receiver for those values. It is possible to create a channel with a buffer.

It is possible to create a channel with a buffer. Buffered channels are blocked only when the buffer is full. Similarly, receiving from a buffered channel are blocked only when the buffer is empty.

We can create Buffered channels by passing an additional capacity parameter to the make function, which specifies the size of the buffer.

ch := make(chan type, capacity)

Here, capacity in the above syntax should be greater than 0 for a channel to have a buffer. The capacity for an unbuffered channel is 0 by default, and hence it omits the capacity parameter.

Let us write some code and create a buffered channel.

package main

import (
    "fmt"
)

func main() {
    ch := make(chan string, 5)
    ch <- "Ashok Kumar"
    ch <- "Sai"
    ch <- "Rama"
    ch <- "Seetha"

    fmt.Println(<-ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

Output

Ashok Kumar 
Sai 
Rama 
Seetha

Run in playground

In the above example, we create a buffered channel with a capacity of 5. Since the channel has a capacity of 5, it is possible to write 5 strings into the channel without being blocked.

We write 4 strings to the channel, and the channel does not block. We read the 4 strings, respectively.

Another Example

Let us look at one more example of the buffered channel in which the values to the channel are written in a concurrent Goroutine and read from the main Goroutine.

This example will help us better understand when writes to a buffered channel block.

package main

import (
    "fmt"
    "time"
)

func write(ch chan int) {
    for i := 0; i < 5; i++ {
        ch <- i
        fmt.Println("Successfully wrote", i, "to channel")
    }
    close(ch)
}

func main() {
    ch := make(chan int, 2)
    go write(ch)
    time.Sleep(1 * time.Second)
    for v := range ch {
        fmt.Println("read value", v, "from channel")
        time.Sleep(1 * time.Second)
    }
}

Output

Successfully wrote 0 to channel 
Successfully wrote 1 to channel 
read value 0 from channel 
Successfully wrote 2 to channel 
read value 1 from channel 
Successfully wrote 3 to channel 
read value 2 from channel 
Successfully wrote 4 to channel 
read value 3 from channel 
read value 4 from channel

Run in playground

In the above example, a buffered channel ch of capacity 2 is created in the main Goroutine and passed to the write Goroutine. Then the main Goroutine sleeps for 1 second.

During this time, the write Goroutine is running concurrently. The write Goroutine has a for loop, which writes numbers from 0 to 4 to the ch channel.

The capacity of this buffered channel is 2. The write Goroutine will be able to immediately write values 0 and 1 to the ch channel, and then it blocks until at least one value is read from the ch channel.

So this program will print the following 2 lines immediately.

Successfully wrote 0 to channel 
Successfully wrote 1 to channel 

After printing the above two lines, the writes to the ch channel in the write Goroutine are blocked until someone reads from the ch channel.

Since the main Goroutine sleeps for 1 second before reading from the channel, the program will not print anything for the next 1 second.

The main Goroutine wakes up after 1 second and starts reading from the ch channel using a for range loop, prints the read value, and then sleeps for 1 second again, and this cycle continues until the ch is closed. So the program will print the following lines after 1 second,

read value 0 from channel 
Successfully wrote 2 to channel 

This will continue until all values are written to the channel and closed in the write Goroutine. The final output is shown in the output section.

Deadlock
package main

import (
    "fmt"
)

func main() {
    ch := make(chan string, 3)
    ch <- "Ashok Kumar"
    ch <- "Sai"
    ch <- "Rama"
    ch <- "Seetha"
    fmt.Println(<-ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

Output

fatal error: all goroutines are asleep - deadlock! 

goroutine 1 [chan send]: 
main.main() 
     /tmp/sandbox206514869/prog.go:12 +0xb9

Run in playground

In the above example, we write 4 strings to a buffered channel of capacity 3. When the control reaches the fourth write, the write is blocked since the channel has exceeded its capacity.

Now some Goroutine must read from the channel for the write to proceed, but in this case, there is no concurrent routine reading from this channel. Hence there will be a deadlock, and the program will panic at run time.

That’s all about the Buffered Channels in Go language. If you have any queries or feedback, please write us email at contact@waytoeasylearn.com. Enjoy learning, Enjoy Go language.!!

Buffered Channels in Go
Scroll to top