Start a goroutine by prefixing a function call with the go keyword, which runs that function concurrently in a new lightweight thread managed by the Go runtime. You must ensure the main function waits for these goroutines to finish, typically using sync.WaitGroup, otherwise the program will exit immediately and terminate the background tasks.
Here is a practical example using sync.WaitGroup to manage multiple goroutines:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Signal completion when the function returns
time.Sleep(time.Second)
fmt.Printf("Worker %d finished\n", id)
}
func main() {
var wg sync.WaitGroup
// Start 3 goroutines
for i := 1; i <= 3; i++ {
wg.Add(1) // Increment counter before starting
go worker(i, &wg)
}
// Block until all goroutines call Done()
wg.Wait()
fmt.Println("All workers completed.")
}
If you need to pass data back from a goroutine, use channels instead of shared variables. Here is a simple pattern for collecting results:
package main
import (
"fmt"
"time"
)
func calculate(id int, resultChan chan int) {
time.Sleep(time.Millisecond * 100)
resultChan <- id * 2
}
func main() {
ch := make(chan int, 3)
for i := 1; i <= 3; i++ {
go calculate(i, ch)
}
// Collect results
for i := 0; i < 3; i++ {
fmt.Println(<-ch)
}
}
Remember that goroutines are extremely cheap to create, so spawning thousands is common, but you must always handle synchronization. If the main function returns before your goroutines finish, the entire process exits, killing them silently. Always use WaitGroup for simple coordination or channels for data exchange to ensure your concurrent logic completes as expected.