Beginner's Tale: Making My Go Tasks Wait Their Turn!๐Ÿšฆ

Beginner's Tale: Making My Go Tasks Wait Their Turn!๐Ÿšฆ

Hey folks, I wanted to share a challenge I faced and how Go helped me out!

The Problem: I had a job that needed to handle loads of data. For me, it was a massive task, processing thousands of data entries. Running them one after the other was just too slow. Trying to do them all at once caused chaos. The solution? The power of Go's Buffered Channels and Semaphores.

How I Fixed It with Buffered Channels: In Go, there's a cool thing called a buffered channel. Think of it like a gatekeeper that only lets a few tasks through at a time.

package main

import (
    "fmt"
    "sync"
)

var limit = make(chan struct{}, 2)

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()

            limit <- struct{}{} // Acquire semaphore
            fmt.Printf("Task %d started\n", id)
            fmt.Printf("Task %d done\n", id)
            <-limit // Release semaphore
        }(i)
    }

    wg.Wait()
}

The idea is simple. The limit <- struct{}{} line allows tasks to take turns. If there's space (imagine it like chairs at a table), a task can sit down and work. Otherwise, it waits for its turn. This ensures that only a few tasks work together at any given time.

While buffered channels worked well, Go's semaphore package from golang.org/x/sync/semaphore is even more flexible, especially when dealing with tasks of different sizes.

package main

import (
    "context"
    "fmt"
    "sync"
    "golang.org/x/sync/semaphore"
)

var sem = semaphore.NewWeighted(2)

func main() {
    var wg sync.WaitGroup
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            if err := sem.Acquire(context.Background(), 1); err != nil {
                return
            }
            fmt.Printf("Task %d started\n", id)
            fmt.Printf("Task %d done\n", id)
            sem.Release(1)
        }(i)
    }
    wg.Wait()
}

Much like the buffered channels, the sem.Acquire() method allows tasks to take their seats at the table. If there's room, they join the party. If not, they wait.

The Outcome: โœจ By introducing controlled concurrency with buffered channels, we made the process much faster, without overloading our system. It was like coordinating a dance, where each dancer knew when to step in.

Conclusion: Whether you use buffered channels or semaphores, Go gives you powerful tools to manage tasks. Think about the challenges you face in your projects. Maybe these methods can help you make your code more elegant and efficient instead of spawning the workers without control.

๐Ÿ“ข Share your coding journeys and the unique challenges you've conquered. Let's learn and grow together!

#Golang #Concurrency #CodingStories

ย