SyntaxStudy
Sign Up
Go Unbuffered and Buffered Channels
Go Beginner 1 min read

Unbuffered and Buffered Channels

Channels are the primary communication mechanism between goroutines in Go. The language's concurrency philosophy, inspired by Tony Hoare's Communicating Sequential Processes, is often summarised as: "Do not communicate by sharing memory; instead, share memory by communicating." Channels make the transfer of data between goroutines explicit, serialised, and safe. An unbuffered channel (created with `make(chan T)`) requires both a sender and a receiver to be ready simultaneously. The send blocks until there is a receiver, and the receive blocks until there is a sender. This synchronisation property makes unbuffered channels useful for signalling and handoffs between goroutines. A buffered channel (created with `make(chan T, capacity)`) allows sends to proceed without a receiver as long as the buffer is not full. Once the buffer is full, sends block until a receiver consumes a value. Buffered channels decouple producers and consumers and can improve throughput when the sender and receiver work at different rates. Choosing the wrong buffer size, however, can hide bugs or create deadlocks.
Example
package main

import (
    "fmt"
    "time"
)

func producer(ch chan<- int, n int) {
    for i := 0; i < n; i++ {
        fmt.Printf("sending %d\n", i)
        ch <- i
        time.Sleep(50 * time.Millisecond)
    }
    close(ch) // signal that no more values will be sent
}

func consumer(ch <-chan int) {
    for v := range ch { // range exits cleanly when channel is closed
        fmt.Printf("received %d\n", v)
    }
}

func main() {
    // Unbuffered channel — synchronous handoff
    unbuf := make(chan int)
    go func() { unbuf <- 42 }()
    v := <-unbuf
    fmt.Println("unbuffered:", v)

    // Buffered channel — asynchronous up to capacity
    buf := make(chan int, 3)
    buf <- 1
    buf <- 2
    buf <- 3
    // All three sends succeed without a receiver (buffer has room)
    fmt.Println("buffered len:", len(buf), "cap:", cap(buf))
    fmt.Println(<-buf, <-buf, <-buf)

    // Pipeline: producer -> consumer
    pipe := make(chan int, 5)
    go producer(pipe, 5)
    consumer(pipe)
}