Thuta Learning
AdvancedProgrammingbeginner

Mini Project

Relax. We'll talk through this in plain words — no textbook voice.

In this mini project, we'll write a pattern where worker goroutines pull jobs from a channel and process them. It's a good foundation for understanding real-world systems like background email senders, image processors, order processors, and queue workers.

go
package main

import (
    "fmt"
    "time"
)

func worker(id int, jobs <-chan string, results chan<- string) {
    for job := range jobs {
        fmt.Println("worker", id, "started", job)
        time.Sleep(200 * time.Millisecond)
        results <- fmt.Sprintf("worker %d finished %s", id, job)
    }
}

func main() {
    jobs := make(chan string, 3)
    results := make(chan string, 3)

    for w := 1; w <= 2; w++ {
        go worker(w, jobs, results)
    }

    for j := 1; j <= 3; j++ {
        jobs <- fmt.Sprintf("job-%d", j)
    }
    close(jobs)

    for r := 1; r <= 3; r++ {
        fmt.Println(<-results)
    }
}

jobs channel gets filled with jobs, and worker goroutines pick them up using range jobs. The results channel sends the finished results back to the main function.

You should see
(Order may vary) worker 2 started job-1 worker 1 started job-2 worker 2 finished job-1 worker 1 finished job-2 worker 2 started job-3 worker 2 finished job-3

Info

jobs <-chan string is a receive-only channel, and results chan<- string is a send-only channel. Specifying direction in the function signature makes your code safer.

Easy traps

  • If you don't call close(jobs), workers can end up waiting forever for more data from the jobs channel. Manage your channel lifecycle carefully.
Mini Project | Thuta Learning