Skip to main content
A goroutine is a lightweight thread of execution managed by the Go runtime. Goroutines are one of Go’s most powerful features, enabling easy concurrent programming with minimal overhead.

Basic Usage

The go keyword launches a function call as a goroutine, allowing it to execute concurrently with the calling code:
package main

import (
	"fmt"
	"time"
)

func f(from string) {
	for i := range 3 {
		fmt.Println(from, ":", i)
	}
}

func main() {
	// Synchronous function call
	f("direct")

	// Launch as a goroutine
	go f("goroutine")

	// Anonymous function as goroutine
	go func(msg string) {
		fmt.Println(msg)
	}("going")

	// Wait for goroutines to complete
	time.Sleep(time.Second)
	fmt.Println("done")
}
Goroutines are extremely lightweight compared to OS threads. You can easily run thousands or even millions of goroutines in a single program.

Key Concepts

Synchronous vs Asynchronous Execution

  • Synchronous: f("direct") - The function runs and completes before the next line executes
  • Asynchronous: go f("goroutine") - The function starts running concurrently, and control returns immediately

Anonymous Function Goroutines

You can launch anonymous functions as goroutines:
go func(msg string) {
	fmt.Println(msg)
}("going")
Arguments to goroutines are evaluated immediately in the calling goroutine, but the function execution happens concurrently.

Waiting for Completion

The example uses time.Sleep() to wait for goroutines, but this is not robust for production code:
time.Sleep(time.Second)
For robust synchronization, use:
  • WaitGroups - Wait for multiple goroutines to complete
  • Channels - Synchronize and communicate between goroutines

Performance Characteristics

FeatureGoroutineOS Thread
Stack size~2KB (growable)~1MB (fixed)
Creation timeMicrosecondsMilliseconds
Context switchNanosecondsMicroseconds
Maximum countMillionsThousands

Common Patterns

Fire and Forget

go processInBackground(data)
// Continue without waiting

Concurrent Processing

for i := range 10 {
	go processItem(i)
}
Goroutines that reference loop variables should be careful about closures. Pass the value as a parameter to avoid race conditions.

Best Practices

  1. Always handle goroutine completion - Don’t let goroutines run indefinitely without a way to signal completion
  2. Use proper synchronization - Prefer WaitGroups or channels over time.Sleep()
  3. Avoid goroutine leaks - Ensure all goroutines can exit when no longer needed
  4. Pass parameters explicitly - Avoid relying on closure capture of loop variables

Build docs developers (and LLMs) love