Exercise 2: Advanced Channels & Inter-Goroutine Communication

Learn how to efficiently manage communication between Goroutines using Channels in Go.

Why Use Channels in Go?

Channels enable safe and efficient communication between Goroutines without requiring explicit locks like `sync.Mutex`.

Types of Channels

  • ✅ **Unbuffered Channels**: Blocks sending until a receiver is available.
  • ✅ **Buffered Channels**: Allows sending multiple values before blocking.

Using Channels in Go

package main

import (
    "fmt"
    "time"
)

func worker(ch chan string) {
    time.Sleep(2 * time.Second)
    ch <- "Task completed!"
}

func main() {
    ch := make(chan string) // Unbuffered Channel
    go worker(ch)
    fmt.Println("Waiting for result...")
    message := <-ch // Retrieve message from the Goroutine
    fmt.Println("Message received:", message)
}

Using `select` for Multiple Channels

The `select` statement allows listening to multiple channels simultaneously.

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(2 * time.Second)
        ch1 <- "Result 1"
    }()

    go func() {
        time.Sleep(1 * time.Second)
        ch2 <- "Result 2"
    }()

    select {
    case msg1 := <-ch1:
        fmt.Println("Received:", msg1)
    case msg2 := <-ch2:
        fmt.Println("Received:", msg2)
    }
}

Exercise Instructions

  • Create a file named channels.go.
  • Implement a program where multiple Workers send results via a Channel.
  • Use `select` to process results in real time.
  • Print the received results to the console.

Exercise Solution

package main

import (
    "fmt"
    "time"
)

func worker(id int, ch chan string) {
    time.Sleep(time.Duration(id) * time.Second)
    ch <- fmt.Sprintf("Worker %d completed", id)
}

func main() {
    numWorkers := 3
    ch := make(chan string, numWorkers)

    for i := 1; i <= numWorkers; i++ {
        go worker(i, ch)
    }

    for i := 1; i <= numWorkers; i++ {
        fmt.Println(<-ch)
    }
}

Solution Explanation

  • Each **Worker** sends a message to a Channel after a delay.
  • The **Buffered Channel** stores results before retrieval.
  • The **program waits** for messages and prints them sequentially.

Best Practices & Common Errors

  • ✅ Always close a Channel after use (`close(ch)`).
  • ✅ Use `select` to avoid blocking Goroutines.
  • ✅ Never write to a closed Channel.
  • ⚠️ Avoid too many Goroutines writing to a single Channel simultaneously.

📚 Learn more about Channels in Go:

Read the Official Documentation ← Previous Exercise 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