Goroutines and Channels
Concurrency is Go's headline feature. A goroutine is a lightweight, independently-running function, and a channel is a typed pipe for goroutines to safely communicate through, instead of sharing memory directly.
Goroutines
func sayHi(name string) {
fmt.Println("Hi,", name)
}
func main() {
go sayHi("Ada") // runs concurrently, doesn't block main
go sayHi("Grace")
time.Sleep(100 * time.Millisecond) // crude wait, channels below do this properly
}
The go keyword launches a function as a goroutine, thousands of them can run concurrently with very little overhead, far lighter-weight than an OS thread.
Channels
Without coordination, main might exit before the goroutines above even run. Channels solve this properly:
func fetchStatus(url string, results chan<- string) {
// pretend this makes a real HTTP request
results <- url + ": OK" // send a value into the channel
}
func main() {
urls := []string{"a.com", "b.com", "c.com"}
results := make(chan string)
for _, url := range urls {
go fetchStatus(url, results)
}
for range urls {
fmt.Println(<-results) // receive a value, blocks until one arrives
}
}
results <- value sends into the channel, <-results receives from it, and receiving blocks until a value is available, this is exactly what replaces time.Sleep guesswork with real synchronization: main waits precisely as long as it needs to, no more, no less.
TIP
Go's own proverb sums up the philosophy: "Don't communicate by sharing memory, share memory by communicating." Channels are the idiomatic way to coordinate goroutines, reaching for shared variables and locks is usually a sign to reach for a channel instead.