DEV Community

loading...

Buffered VS Unbuffered Channel in Golang

clavinjune profile image Clavin June Originally published at clavinjune.dev on ・3 min read

Photo by @quinoal on Unsplash

Small talk about channel

If we talk about concurrency in Golang, Golang provides us with a type of concurrency communication called Channel. Channel itself helps us to communicate between goroutines. We can send and receive messages from one goroutine to another. There are two types of channel in golang that we can used and let’s talk about them.

Unbuffered Channel

Unbuffered channel is a channel that initially has no capacity to store message inside it. Unbuffered channel requires us to fill the message in order to make the goroutine process unblocked by the channel. For example:

package main

import (
  "fmt"
  "time"
)

func access(ch chan int) {
  time.Sleep(time.Second)
  fmt.Println("start accessing channel\n")

  for i := range ch {
    fmt.Println(i)
    time.Sleep(time.Second)
  }
}

func main() {
  ch := make(chan int)
  defer close(ch)

  go access(ch)

  for i := 0; i < 9; i++ {
    ch <- i
    fmt.Println("Filled")
  }

  time.Sleep(3 * time.Second)
}
Enter fullscreen mode Exit fullscreen mode

In the example above, our main function blocked when filling ch one by one. And our access function will print the message filled by main function one by one also because of the for loop.

Here is the output of the example above:

$ go run chan.go 
start accessing channel

0
Filled
1
Filled
2
Filled
3
Filled
4
Filled
5
Filled
6
Filled
7
Filled
8
Filled
Enter fullscreen mode Exit fullscreen mode

Buffered Channel

Unlike the Unbuffered Channel, Buffered Channel has a capacity to store messages inside it. Buffered Channel could be filled up to its defined capacity, not only one message. For example:

package main

import (
  "fmt"
  "time"
)

func access(ch chan int) {
  time.Sleep(time.Second)
  fmt.Println("start accessing channel\n")

  for i := range ch {
    fmt.Println(i)
    time.Sleep(time.Second)
  }
}

func main() {
  // only modify this line to defined the capacity
  ch := make(chan int, 3)
  defer close(ch)

  go access(ch)

  for i := 0; i < 9; i++ {
    ch <- i
    fmt.Println("Filled")
  }

  time.Sleep(3 * time.Second)
}
Enter fullscreen mode Exit fullscreen mode

Here is the output:

$ go run chan.go
Filled
Filled
Filled
start accessing channel

0
Filled
1
Filled
2
Filled
3
Filled
4
Filled
5
Filled
6
7
8
Enter fullscreen mode Exit fullscreen mode

As you can see, the ch could be filled first until it is full-capacity then the other goroutine could access it one by one.

Differences

Unbuffered Channel has no capacity initially, but Buffered Channel has a capacity.

Unbuffered Channel will block the goroutine whenever it is empty and waiting to be filled. But Buffered Channel will also block the goroutine either when it is empty and waiting to be filled or it's on its full-capacity and there's a statement that want to fill the channel.

Capacity

unbuffered := make(chan int)

buffered := make(chan int, 30)
Enter fullscreen mode Exit fullscreen mode

Empty and Waiting to be filled

Both Buffered and Unbuffered channel blocks the goroutine until it is filled in this case blocks for a second.

func main() {
  ch := make(chan int)
  defer close(ch)

  go func(ch chan int) {
    time.Sleep(time.Second)
    ch <- 1
  }(ch)

  <-ch
}
Enter fullscreen mode Exit fullscreen mode

Full Capacity and there’s statement that want to fill the channel

Only Buffered channel blocks on ch <- 2 because the ch is on its full capacity and waiting to be released. The Unbuffered Channel will go deadlock this time because there’s no goroutine accessing the message 2 but it is filled.

func main() {
  ch := make(chan int, 1)
  defer close(ch)

  go func(ch chan int) {
    time.Sleep(time.Second)
    <-ch
  }(ch)

  ch <- 1
  ch <- 2
}
Enter fullscreen mode Exit fullscreen mode

Discussion (0)

pic
Editor guide