Skip to main content

Command Palette

Search for a command to run...

Golang Concurrency #4 - Channel operations

Published
2 min read
Golang Concurrency #4 - Channel operations
T

Just a guy who loves to write code and watch anime.

Send and Receive (Covered in previous post)

ch <- value    // Send
value := <-ch  // Receive

Closing Channels

close(ch)
  • Signals "no more values will be sent"

  • Receivers can still read remaining values

  • Sending to closed channel = panic

Checking if channel is closed

value, ok := <-ch
if !ok {
    // Channel is closed and empty
}

Buffered vs Unbuffered Channels

Unbuffered (default)

ch := make(chan int)  // Buffer size 0
  • Sender blocks until receiver is ready

  • Receiver blocks until sender sends

  • Synchronous handoff

Buffered

ch := make(chan int, 3)  // Buffer size 3
  • Sender only blocks when buffer is full

  • Receiver only blocks when buffer is empty

  • Asynchronous up to buffer size

Buffered example

ch := make(chan int, 2)

ch <- 1  // Doesn't block, buffer has space
ch <- 2  // Doesn't block, buffer has space
ch <- 3  // BLOCKS! Buffer is full

fmt.Println(<-ch)  // Prints 1, frees buffer space
ch <- 3            // Now this works

When to use buffered

  • When you want to decouple sender/receiver timing

  • When you know how many items you'll process

  • For performance (reduce blocking)

When to use unbuffered

  • When you want synchronization (sender waits for receiver)

  • For simple request-response patterns

  • When you want guaranteed delivery

More notes

Receiving removes from channel

ch := make(chan int, 2)
ch <- 10
ch <- 20

first := <-ch   // Takes 10 out of channel
second := <-ch  // Takes 20 out of channel
third := <-ch   // Channel is now empty, this blocks

Once you receive a value, it's gone from the channel.

Buffer is exactly like a queue (FIFO - First In, First Out)

ch := make(chan int, 3)

ch <- 1  // Queue: [1]
ch <- 2  // Queue: [1, 2]
ch <- 3  // Queue: [1, 2, 3]

fmt.Println(<-ch)  // Gets 1, Queue: [2, 3]
fmt.Println(<-ch)  // Gets 2, Queue: [3]
fmt.Println(<-ch)  // Gets 3, Queue: []

Non-blocking = other code runs

ch := make(chan int, 2)

ch <- 1         // Doesn't block, continues immediately
ch <- 2         // Doesn't block, continues immediately
fmt.Println("This runs right away!")

ch <- 3         // NOW it blocks, waiting for buffer space
fmt.Println("This waits until someone receives")

Blocking = everything stops

ch := make(chan int)  // Unbuffered

ch <- 1  // BLOCKS here, waiting for receiver
fmt.Println("This never runs until someone receives")