Buffered and unbuffered channels in Go - Time & Space Complexity
When using channels in Go, it's important to understand how sending and receiving messages affects program speed.
We want to see how the time to send and receive changes as we increase the number of messages.
Analyze the time complexity of sending messages through buffered and unbuffered channels.
package main
func main() {
ch := make(chan int, 3) // buffered channel with capacity 3
for i := 0; i < 5; i++ {
ch <- i // send value to channel
}
}
This code sends 5 integers into a buffered channel that can hold 3 items before blocking.
Look at what repeats as the program runs.
- Primary operation: Sending values into the channel inside the loop.
- How many times: 5 times, once per loop iteration.
As the number of messages to send increases, the time to send grows depending on channel type.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 sends; some may block if unbuffered |
| 100 | About 100 sends; blocking happens often if unbuffered |
| 1000 | About 1000 sends; blocking dominates if unbuffered |
Pattern observation: Each send is done once, but blocking can add waiting time especially for unbuffered channels.
Time Complexity: O(n)
This means the time to send n messages grows roughly in direct proportion to n.
[X] Wrong: "Buffered channels always make sending instant and constant time."
[OK] Correct: Buffered channels only delay blocking until the buffer is full; after that, sending still waits, so time grows with messages.
Understanding how channels behave with different buffering helps you reason about program speed and concurrency, a useful skill in many coding challenges.
"What if we changed the buffered channel size to be equal to the number of messages? How would the time complexity change?"
Practice
Solution
Step 1: Understand unbuffered channel behavior
Unbuffered channels require the sender to wait until the receiver is ready to receive the value.Step 2: Understand buffered channel behavior
Buffered channels have a capacity to hold multiple values, so the sender can send without waiting until the buffer is full.Final Answer:
Buffered channels allow sending multiple values without waiting, unbuffered channels require receiver ready. -> Option AQuick Check:
Buffered vs unbuffered = sender wait or not [OK]
- Thinking unbuffered channels store values internally
- Believing buffered channels never block sender
- Confusing data types allowed in channels
Solution
Step 1: Recall syntax for buffered channel creation
Buffered channels are created with make(chan Type, capacity). Here, capacity is 3.Step 2: Check each option
ch := make(chan int, 3) uses make(chan int, 3) which is correct. ch := make(chan int) creates unbuffered channel. ch := make(chan int[3]) has invalid syntax. ch := new(chan int, 3) uses new which is incorrect for channels.Final Answer:
ch := make(chan int, 3) -> Option CQuick Check:
Buffered channel syntax = make(chan Type, capacity) [OK]
- Omitting capacity creates unbuffered channel
- Using new() instead of make()
- Incorrect syntax with array notation
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
ch <- 10
}()
fmt.Println(<-ch)
}Solution
Step 1: Analyze channel type and goroutine
The channel ch is unbuffered. The goroutine sends 10 to ch, and main receives from ch.Step 2: Understand synchronization
Because the send and receive happen in separate goroutines, the send blocks until receive is ready, so the program runs without deadlock and prints 10.Final Answer:
10 -> Option BQuick Check:
Unbuffered channel with goroutine send = prints sent value [OK]
- Expecting deadlock without goroutine
- Thinking unbuffered channel stores default zero
- Confusing compilation error with runtime behavior
ch := make(chan int, 2) ch <- 1 ch <- 2 ch <- 3 fmt.Println(<-ch)What is the problem with this code?
Solution
Step 1: Check channel buffer capacity and sends
The channel has capacity 2. Two sends fill the buffer. The third send blocks because buffer is full and no receiver yet.Step 2: Understand blocking behavior
Since no goroutine is receiving before the third send, the program deadlocks at third send.Final Answer:
It causes a deadlock because the buffered channel is full before the third send. -> Option DQuick Check:
Buffered channel full + send without receive = deadlock [OK]
- Assuming buffer overwrites old values
- Expecting zero printed from empty channel
- Confusing syntax error with runtime deadlock
Option A:
ch := make(chan int, 5)
for i := 1; i <= 5; i++ {
ch <- i
}
Option B:
ch := make(chan int)
for i := 1; i <= 5; i++ {
ch <- i
}
Option C:
ch := make(chan int, 3)
for i := 1; i <= 5; i++ {
ch <- i
}
Option D:
ch := make(chan int, 0)
for i := 1; i <= 5; i++ {
ch <- i
}Solution
Step 1: Understand buffered channel capacity
To send 5 integers without blocking immediately, the channel buffer must be at least 5.Step 2: Analyze each snippet's buffer size
The first snippet creates a channel with capacity 5, allowing all 5 sends without blocking. The second creates an unbuffered channel, blocking the sender immediately on the first send. The third has capacity 3, blocking on the fourth send. The fourth has capacity 0 (unbuffered), blocking immediately.Final Answer:
Option A only -> Option AQuick Check:
Buffer size >= sends to avoid blocking [OK]
- Using unbuffered channels expecting no blocking
- Choosing smaller buffer than sends
- Misunderstanding buffer size zero means unbuffered
Ready to prove it?
Pass the test and this pattern moves unknown
