Go Goroutines

Lightweight concurrent functions in Go

⚡ What are Goroutines?

Goroutines are lightweight threads managed by Go runtime. They enable concurrent execution with minimal memory overhead, allowing thousands to run simultaneously in a single program efficiently.


// Basic goroutine example
package main

import (
    "fmt"
    "time"
)

func main() {
    go printNumbers()
    go printLetters()
    time.Sleep(3 * time.Second)
}

func printNumbers() {
    for i := 1; i <= 5; i++ {
        fmt.Printf("Number: %d\n", i)
        time.Sleep(500 * time.Millisecond)
    }
}

func printLetters() {
    for i := 'A'; i <= 'E'; i++ {
        fmt.Printf("Letter: %c\n", i)
        time.Sleep(700 * time.Millisecond)
    }
}
                                    

Goroutine Features

ðŸŠķ

Lightweight

Only 2KB memory per goroutine

// Start 1000 goroutines easily
for i := 0; i < 1000; i++ {
    go task(i)
}
🔄

Managed

Go runtime handles scheduling

// No manual thread management
go func() {
    // Work happens here
}()
📈

Scalable

Thousands can run simultaneously

// Multiplexed onto OS threads
runtime.GOMAXPROCS(
    runtime.NumCPU()
)
ðŸŽŊ

Simple

Just add 'go' before function call

// Regular function call
doWork()
// Goroutine
go doWork()

ðŸ”đ Creating Goroutines

Start a goroutine by adding 'go' before any function call:

package main

import (
    "fmt"
    "time"
)

func main() {
    // Regular function call - runs sequentially
    sayHello("Alice")
    sayHello("Bob")
    
    // Goroutine calls - run concurrently
    go sayHello("Charlie")
    go sayHello("Diana")
    
    // Anonymous goroutine
    go func(name string) {
        fmt.Printf("Hello from anonymous goroutine, %s!\n", name)
    }("Eve")
    
    // Wait for goroutines to complete
    time.Sleep(2 * time.Second)
}

func sayHello(name string) {
    for i := 0; i < 3; i++ {
        fmt.Printf("Hello %s! (%d)\n", name, i+1)
        time.Sleep(500 * time.Millisecond)
    }
}

Output:

Hello Alice! (1)
Hello Alice! (2)
Hello Alice! (3)
Hello Bob! (1)
Hello Bob! (2)
Hello Bob! (3)
Hello Charlie! (1)
Hello Diana! (1)
Hello from anonymous goroutine, Eve!
Hello Charlie! (2)
Hello Diana! (2)
Hello Charlie! (3)
Hello Diana! (3)

ðŸ”đ WaitGroup for Synchronization

Use sync.WaitGroup to wait for goroutines to complete:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup
    
    tasks := []string{"Download", "Process", "Upload"}
    
    for _, task := range tasks {
        wg.Add(1) // Increment counter
        go performTask(task, &wg)
    }
    
    wg.Wait() // Wait for all goroutines
    fmt.Println("All tasks completed!")
}

func performTask(taskName string, wg *sync.WaitGroup) {
    defer wg.Done() // Decrement counter when done
    
    fmt.Printf("Starting %s...\n", taskName)
    time.Sleep(time.Duration(len(taskName)) * 200 * time.Millisecond)
    fmt.Printf("Completed %s!\n", taskName)
}

Output:

Starting Download...
Starting Process...
Starting Upload...
Completed Upload!
Completed Process!
Completed Download!
All tasks completed!

ðŸ”đ Goroutine Best Practices

Follow these practices for effective goroutine usage:

✅ Do:

  • Use WaitGroup: Wait for goroutines to complete
  • Handle errors: Don't ignore goroutine errors
  • Limit goroutines: Don't create unlimited goroutines
  • Use channels: For communication between goroutines

❌ Don't:

  • Use time.Sleep: For synchronization in production
  • Share memory: Without proper synchronization
  • Ignore goroutine leaks: Always ensure goroutines can exit
// Good: Controlled goroutine creation
package main

import (
    "fmt"
    "sync"
)

func main() {
    const maxGoroutines = 10
    var wg sync.WaitGroup
    semaphore := make(chan struct{}, maxGoroutines)
    
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            semaphore <- struct{}{} // Acquire
            defer func() { <-semaphore }() // Release
            
            // Do work here
            fmt.Printf("Worker %d working\n", id)
        }(i)
    }
    
    wg.Wait()
}

🧠 Test Your Knowledge

How much memory does a goroutine typically use?