Goroutines and Channels
Go's lightweight concurrency primitives, and how channels coordinate them safely.
What you'll learn
- Start a concurrent goroutine with the `go` keyword
- Use a channel to safely communicate a value between goroutines
- Explain why Go's motto is 'share memory by communicating' rather than locking shared memory directly
Explanation
A goroutine is a lightweight, independently-scheduled function execution -- you start one just by prefixing a function call with go: go doWork(). Goroutines are far cheaper than OS threads (a Go program can comfortably run tens of thousands of them), managed by Go's own runtime scheduler.
Goroutines alone are dangerous without coordination -- reading and writing the same variable from multiple goroutines at once is a data race. Go's answer is the channel: a typed conduit for sending values between goroutines safely, created with make(chan int). One goroutine sends with ch <- value, another receives with value := <-ch -- both operations block until the other side is ready, which naturally synchronizes the two goroutines.
This reflects Go's concurrency motto: "Don't communicate by sharing memory; share memory by communicating." Instead of multiple goroutines directly touching the same variable behind a lock, they pass values to each other through channels, so only one goroutine ever "owns" a piece of data at a time.
This walkthrough starts from a plain sequential version, then adds a goroutine and a channel step by step so you can see exactly what each addition changes.
Guided lab
Guided edit: From sequential to a goroutine with a channel
Follow each step to see how adding a goroutine and a channel changes this program.
Step 1 of 2
Start with a plain sequential function call -- no concurrency yet.
package main
import "fmt"
func square(n int) int {
return n * n
}
func main() {
result := square(6)
fmt.Println("Result:", result)
}Stuck? Get a hint.
Common mistakes
- Starting a goroutine and letting `main` return before it finishes -- when `main` returns, the whole program exits immediately, goroutines and all.
- Reading/writing a shared variable from multiple goroutines directly without a channel or lock, causing a data race.
- Forgetting that both a channel send and receive block until the other side is ready (for an unbuffered channel), which can deadlock a program with no one else to receive/send.
Knowledge check
Takeaway
Use a channel to hand a value from one goroutine to another safely -- and remember that `main` returning ends the whole program, unfinished goroutines included.
Summary
Goroutines are lightweight concurrent function calls started with `go`; channels safely pass values between them, embodying Go's 'share memory by communicating' philosophy.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.