Concurrency in Go
Go is famous for its concurrency model. Unlike many other languages that treat concurrency as an afterthought or a library add-on, Go builds concurrency directly into the language core.Goroutines
A goroutine is a lightweight thread managed by the Go runtime.The go Keyword
To start a goroutine, simply use the go keyword before a function call.
How Goroutines Work
Goroutines are multiplexed onto a smaller number of OS threads using the GMP model (Goroutines, Machine threads, Processors). This M:N scheduling allows Go to efficiently run thousands of goroutines on just a handful of OS threads. Key Characteristics:- Lightweight: Start with a tiny 2KB stack that grows/shrinks dynamically.
- Fast Context Switching: Switching between goroutines is much faster than OS thread context switches.
- Work Stealing: Idle processors can steal work from busy ones for load balancing.
Channels
Channels are the pipes that connect concurrent goroutines. You can send values into channels from one goroutine and receive those values into another goroutine.Creating Channels
Sending and Receiving
Unbuffered vs. Buffered Channels
- Unbuffered Channels: Sending blocks until the receiver is ready. Receiving blocks until the sender is ready. This provides synchronization.
- Buffered Channels: Sending only blocks if the buffer is full. Receiving only blocks if the buffer is empty.
Channel Internals
Under the hood, a channel is a structhchan that contains a circular buffer, send/receive indices, and wait queues for blocked goroutines.
The select Statement
The select statement lets a goroutine wait on multiple communication operations. A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.
Synchronization Primitives (sync package)
While channels are great for passing data, sometimes you just need to coordinate state.
WaitGroup
sync.WaitGroup waits for a collection of goroutines to finish.
Mutex
sync.Mutex provides a mutual exclusion lock to prevent data races.
Best Practices
- Share Memory by Communicating: Don’t communicate by sharing memory. Use channels to pass ownership of data.
- Detect Race Conditions: Run your tests with
go test -raceto detect data races. - Avoid Leaking Goroutines: Ensure every goroutine you start has a way to exit.
Summary
- Goroutines are lightweight threads managed by the Go runtime.
- Channels allow safe communication and synchronization between goroutines.
- Select allows waiting on multiple channel operations.
- Sync Package provides low-level primitives like
MutexandWaitGroupfor finer control.