Essential Go Channels and select  Suggest an edit

Buffered vs. unbuffered channels

By default channels are unbuffered.

Sending and receiving goroutines block unless sending goroutine has a value to send and receiving goroutine is ready to receive.

Insisting on synchronization for every receive/send operation might introduce unnecessary slowdowns.

Imagine a scenario where one worker produces values and another worker consumes it.

If it takes a second to produce a value and a second to consume it, it takes 2 * N seconds to produce and consume all values.

If producer can queue up multiple values in the channel, producer doesn’t have to wait for consumer to be ready for each value.

This is a job for buffered channels.

By allowing producer to proceed independently of the consumer we can speed up some scenarios.

func producer(ch chan int) {
	for i := 0; i < 5; i++ {
		if i%2 == 0 {
			time.Sleep(10 * time.Millisecond)
		} else {
			time.Sleep(1 * time.Millisecond)
		}
		ch <- i
	}
}

func consumer(ch chan int) {
	total := 0
	for i := 0; i < 5; i++ {
		if i%2 == 1 {
			time.Sleep(10 * time.Millisecond)
		} else {
			time.Sleep(1 * time.Millisecond)
		}
		total += <-ch
	}
}

func unbuffered() {
	timeStart := time.Now()
	ch := make(chan int)
	go producer(ch)
	consumer(ch)
	fmt.Printf("Unbuffered version took %s\n", time.Since(timeStart))
}

func buffered() {
	timeStart := time.Now()
	ch := make(chan int, 5)
	go producer(ch)
	consumer(ch)
	fmt.Printf("Buffered version took %s\n", time.Since(timeStart))
}

func main() {
	unbuffered()
	buffered()
}
Unbuffered version took 60.521301ms
Buffered version took 38.647922ms
  ↑ ↓ to navigate     ↵ to select     Esc to close