Go

Goroutines & Channels

This is the feature Go is most associated with, and the one Introduction to Go named as the language's central design goal. A goroutine is an extremely cheap, independently-scheduled function call; a channel is a typed pipe two goroutines use to pass data safely, without touching a mutex directly.

Quick reference
What it is
A goroutine is a lightweight, concurrently-executing function, managed by Go's own runtime rather than the OS directly. A channel is a typed conduit for sending values between goroutines.
Mental model
Go's own slogan for it: "don't communicate by sharing memory; share memory by communicating" — pass data through a channel instead of having two threads both reach into the same shared variable.
Key numbers
A goroutine starts with only a few KB of stack (growing as needed) — genuinely cheap to spawn thousands of, unlike a full OS thread.
Reach for it when
Work can happen concurrently (multiple network requests, parallel processing) and you'd otherwise reach for OS threads plus manual locking in C/C++.

Goroutines

func sayHello() {
    fmt.Println("Hello from a goroutine")
}

func main() {
    go sayHello()   // the "go" keyword — runs sayHello CONCURRENTLY, doesn't wait for it
    fmt.Println("Hello from main")
    time.Sleep(100 * time.Millisecond)   // give the goroutine time to run before main exits
}

The go keyword is the entire mechanism — prefixing any function call with it launches that call as a new goroutine, running concurrently with everything else. This is dramatically lighter-weight than spawning an OS thread in C/C++, which is exactly why Go programs commonly run thousands of goroutines at once without a second thought.

Channels

ch := make(chan int)   // a channel that carries int values

go func() {
    ch <- 42   // SEND 42 into the channel
}()

value := <-ch   // RECEIVE from the channel — blocks until something is sent
fmt.Println(value)   // 42

Sending and receiving both block by default until the other side is ready — this is the actual safety mechanism. Instead of two goroutines racing to read and write the same shared variable (exactly the hazard a mutex exists to prevent in C/C++), they hand a value through the channel, one at a time, with the language itself enforcing the handoff.

The producer/consumer pattern, visually

A producer goroutine sends values into a channel; a consumer goroutine receives them on the other end, one at a time producer goroutine channel chan int consumer goroutine Both sides block until the other is ready — this is the safety guarantee, built into the language
No shared variable, no mutex to remember to lock — the channel itself is the synchronization mechanism.

select — waiting on multiple channels

select {
case msg1 := <-channel1:
    fmt.Println("received from channel1:", msg1)
case msg2 := <-channel2:
    fmt.Println("received from channel2:", msg2)
case <-time.After(1 * time.Second):
    fmt.Println("timed out waiting")
}

select waits on multiple channel operations at once, proceeding with whichever one becomes ready first — Go's built-in tool for exactly the "wait on several things simultaneously, plus a timeout" problem that's much more manual to build in C/C++.

Real-world applications

Gotchas

Field notes