Go's killer feature is built-in concurrency with goroutines and channels. Concurrency in Go is not about threads — it is about communicating between lightweight processes.
Goroutines
A goroutine is a lightweight thread managed by the Go runtime:
Go
func say(name string) {
for i := 0; i < 3; i++ {
fmt.Printf("%s: %d\n", name, i)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
go say("RAM") // launches in background
go say("SITA") // launches in background
say("MAIN") // runs in foreground
time.Sleep(500 * time.Millisecond)
}Cost: a goroutine starts with just 2KB of stack (vs 1MB for an OS thread). You can run millions of goroutines.
Channels
Channels are typed pipes that connect goroutines:
Go
ch := make(chan string)
// Goroutine 1: sends data
go func() {
ch <- "Hello from goroutine" // send
}()
// Goroutine 2: receives data
msg := <-ch // receive (blocks until data arrives)
fmt.Println(msg)Buffered Channels
A buffered channel holds data until it is full:
Go
ch := make(chan int, 3) // buffer size 3
ch <- 1
ch <- 2
ch <- 3
// ch <- 4 // would block — buffer is fullRange over Channels
Go
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch) // signal that no more data is coming
}()
for val := range ch {
fmt.Println(val)
}Select
select waits on multiple channel operations — like a switch for
channels:
Go
select {
case msg := <-ch1:
fmt.Println("From ch1:", msg)
case ch2 <- 42:
fmt.Println("Sent to ch2")
case <-time.After(1 * time.Second):
fmt.Println("Timeout!")
}WaitGroup
Wait for a group of goroutines to finish:
Go
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Worker %d done\n", id)
}(i)
}
wg.Wait() // blocks until all goroutines call Done()Common Patterns
Fan-out / Fan-in
Split work across goroutines, collect results:
Go
func process(urls []string) []Result {
results := make(chan Result, len(urls))
for _, url := range urls {
go func(u string) {
results <- fetch(u)
}(url)
}
var out []Result
for range urls {
out = append(out, <-results)
}
return out
}Tips
- Communicate through channels — do not share memory.
- Use
selectfor timeouts and multiplexing. - Use
sync.WaitGroupfor simple "wait for all" patterns. - Start with goroutines + channels before reaching for mutexes.
🎉 You have completed the Go course! The next step is to build something — a REST API with the standard library, a CLI tool, or a microservice.