Exercise 1: Advanced Concurrency in Go

Learn how to optimize Goroutine management using Workers, Mutex, and WaitGroup.

Why Optimize Concurrency in Go?

Go makes concurrency easy with Goroutines, but poor management can lead to data conflicts or excessive resource consumption.

Exercise Instructions

  • Create a file named concurrency.go.
  • Implement a worker pool that executes tasks from a Channel.
  • Use `sync.WaitGroup` to wait for all tasks to finish.
  • Use `sync.Mutex` to protect a shared variable.
  • Print the final result after all tasks are executed.

Exercise Solution

package main

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

const numWorkers = 3
const numTasks = 6

func worker(id int, tasks <-chan int, wg *sync.WaitGroup, mutex *sync.Mutex, result *int) {
    defer wg.Done()
    for task := range tasks {
        fmt.Printf("Worker %d processing task %d\n", id, task)
        time.Sleep(time.Second)
        mutex.Lock()
        *result += task // Protect concurrent access with Mutex
        mutex.Unlock()
    }
}

func main() {
    tasks := make(chan int, numTasks)
    var wg sync.WaitGroup
    var mutex sync.Mutex
    var result int

    // Launch Workers
    for i := 1; i <= numWorkers; i++ {
        wg.Add(1)
        go worker(i, tasks, &wg, &mutex, &result)
    }

    // Send tasks
    for i := 1; i <= numTasks; i++ {
        tasks <- i
    }
    close(tasks)

    // Wait for all Goroutines to finish
    wg.Wait()
    fmt.Println("Sum of executed tasks:", result)
}

Solution Explanation

  • We created a **Channel `tasks`** to store tasks.
  • We defined **3 Workers** to execute tasks in parallel.
  • A **WaitGroup (`sync.WaitGroup`)** ensures the program waits for all Workers to finish.
  • A **Mutex (`sync.Mutex`)** protects concurrent access to the `result` variable.
  • Tasks are executed, and we print the sum of processed tasks.

Best Practices & Common Errors

  • ✅ Always use `sync.WaitGroup` to ensure the program doesn't exit before all Goroutines finish.
  • ✅ Use `sync.Mutex` to protect shared resources.
  • ✅ Avoid launching too many Goroutines at once; use a Worker Pool instead.
  • ✅ Use `context.Context` for better Goroutine cancellation management.
  • ⚠️ Always **close Channels** to avoid Goroutine deadlocks.

📚 Learn more about Go concurrency:

Read the Official Documentation Next Exercise →

🚀 Enjoying these exercises? If you find them useful and want to support my work, buying me a coffee would be greatly appreciated! ☕😊


☕ Buy me a coffee