Goroutines
Goroutines are lightweight threads of execution in the Go programming language, enabling concurrent programming by running multiple tasks simultaneously. They are a key feature in Go, designed to simplify concurrent programming and improve efficiency. Here's an in-depth look, including examples:
What are Goroutines?
A goroutine is a function or method that executes concurrently with other goroutines. They are managed by the Go runtime rather than the operating system, making them lightweight compared to traditional threads. Goroutines are started with the go
keyword.
Key Features:
Lightweight: Thousands of goroutines can run simultaneously in the same application.
Dynamic Scheduling: The Go runtime handles scheduling, allowing efficient use of resources.
Communication: Goroutines communicate and synchronize using channels.
How to Create a Goroutine
To start a goroutine, use the go
keyword before a function call.
Output: The output alternates between "Hello" and "World" because sayHello
and the main function run concurrently.
Note that the main function will not wait for the goroutine to finish. If you want to wait for a goroutine to complete, you can use synchronization mechanisms like channels or sync.WaitGroup
.
If the main function exits, all goroutines are terminated. That's the reason why the sleep is longer in the main function than in the goroutine.
Anonymous Goroutines
You can start a goroutine with an anonymous function:
Output: The "Inside goroutine" messages appear while the main function is running.
Using Channels with Goroutines
Channels are used to communicate between goroutines safely.
Example 1: Sending and Receiving Data
Output:
Example 2: Buffered Channels
Goroutines and Synchronization
Without proper synchronization, goroutines can cause race conditions. Use the sync.WaitGroup
to wait for multiple goroutines to finish.
Common Use Cases
Web Servers Goroutines handle multiple client requests concurrently in web servers.
package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World!") } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }Each HTTP request is handled in its own goroutine internally.
Parallel Processing Splitting tasks across multiple goroutines for parallel computation.
package main import ( "fmt" "sync" ) func compute(wg *sync.WaitGroup, id int) { defer wg.Done() fmt.Printf("Task %d started\n", id) } func main() { var wg sync.WaitGroup for i := 1; i <= 3; i++ { wg.Add(1) go compute(&wg, i) } wg.Wait() fmt.Println("All tasks completed") }
Key Considerations
Avoid Goroutine Leaks: Ensure goroutines terminate or have a proper way to exit to avoid memory leaks.
Shared State: Use channels or sync primitives (e.g.,
sync.Mutex
) to manage shared data.Panic Recovery: Use
defer
andrecover
to handle panics within goroutines.
By using goroutines effectively, you can build highly concurrent and efficient applications in Go.