Problem Statement
Master all channel operations and their edge cases. This knowledge is essential for writing correct concurrent Go programs.
Channel Behavior Reference
| Operation | Nil Channel | Closed Channel | Open Channel |
|---|---|---|---|
ch <- v (send) | Blocks forever | PANIC | Blocks until received |
<-ch (receive) | Blocks forever | Returns zero, false | Blocks until sent |
close(ch) | PANIC | PANIC | Closes successfully |
Key Interview Questions
Q1: What happens if you send to a closed channel?
ch := make(chan int)
close(ch)
ch <- 1 // PANIC: send on closed channelQ2: What happens if you receive from a closed channel?
ch := make(chan int, 1)
ch <- 42
close(ch)
fmt.Println(<-ch) // 42
fmt.Println(<-ch) // 0 (zero value)
// Detect closed channel:
val, ok := <-ch
if !ok {
fmt.Println("channel closed")
}Q3: What happens if you send/receive on a nil channel?
var ch chan int // nil
// ch <- 1 // Blocks forever
// <-ch // Blocks forever
// close(ch) // PANICUse case: Disable a select case dynamically by setting channel to nil.
Q4: How do you implement a non-blocking send?
select {
case ch <- value:
fmt.Println("sent")
default:
fmt.Println("channel full, dropped")
}Q5: Buffered vs Unbuffered channels
// Unbuffered: synchronous handoff
ch := make(chan int)
// Sender blocks until receiver is ready
// Buffered: async up to capacity
ch := make(chan int, 10)
// Sender blocks only when buffer is fullQ6: How do you drain a channel after closing?
close(ch)
for val := range ch {
process(val)
}
// range stops when channel is closed AND emptyQ7: Implement timeout with select
select {
case result := <-ch:
fmt.Println("got result:", result)
case <-time.After(5 * time.Second):
fmt.Println("timeout!")
}Q8: Can you close a receive-only channel?
func consumer(ch <-chan int) {
// close(ch) // COMPILE ERROR
}
// Only the sender should close channels