Sling Academy
Home/Golang/Implementing Thread Pools with Goroutines

Implementing Thread Pools with Goroutines

Last updated: November 27, 2024

In this article, we will explore how to implement a thread pool using Goroutines in Go (Golang). A thread pool is a collection of pre-instantiated reusable threads, which makes it more efficient when it comes to executing tasks concurrently. Using a thread pool can help avoid the overhead of constantly creating and destroying threads.

Understanding Goroutines

Goroutines are a lightweight and efficient way to achieve concurrency in Go. Spawning a Goroutine is as easy as calling a function with the go keyword. They are managed by the Go runtime, which optimizes their execution for performance and lower resource usage.

Setting Up a Simple Worker Pool

To implement a basic thread pool using Goroutines, we'll define a few key components: a Job struct, a Job queue, and Worker Goroutines.

Defining a Job

A Job can be any task that you want to execute concurrently. For simplicity, let's define it to hold data and the execution logic it should process:

type Job struct {
    ID   int
    Task func()
}

Creating a Worker

A Worker is a Goroutine that picks up jobs from a queue and executes them. Below is a simple implementation of a worker:

func worker(id int, jobs <-chan Job, results chan<- int) {
    for job := range jobs {
        fmt.Printf("Worker %d processing job %d\n", id, job.ID)
        job.Task()  // Execute the job's task
        results <- job.ID // We're sending back a simple result
    }
}

Implementing the Job Queue

Now that we have our Job and Worker defined, let's create a way to distribute jobs:

func startDispatcher(numWorkers int, jobsQueue []Job, results chan<- int) {
    jobs := make(chan Job, len(jobsQueue))

    for i := 0; i < numWorkers; i++ {
        go worker(i, jobs, results)
    }

    for _, job := range jobsQueue {
        jobs <- job
    }
    close(jobs)
}

In this function, we create a channel of jobs and dispatch a specified number of worker Goroutines. Each worker consumes jobs until the queue is empty.

Putting it All Together

Let's see how all these components work together in a simple main function:

func main() {
    jobsQueue := []Job{
        {ID: 1, Task: func() { fmt.Println("Executing task 1") }},
        {ID: 2, Task: func() { fmt.Println("Executing task 2") }},
        {ID: 3, Task: func() { fmt.Println("Executing task 3") }},
    }

    results := make(chan int, len(jobsQueue))
    numWorkers := 2

    go startDispatcher(numWorkers, jobsQueue, results)

    // Collect results
    for a := 0; a < len(jobsQueue); a++ {
        result := <-results
        fmt.Printf("Job %d completed\n", result)
    }
}

In this main function, we define a queue of jobs where each job prints out a message. We then start the dispatcher with a specified number of workers and handle completion logging for each job.

Conclusion

Using Goroutines to create a thread pool in Go allows executing multiple jobs concurrently and efficiently. With only a few lines of code, we can set up a system that executes jobs across several worker Goroutines, making better use of system resources without much complexity.

Next Article: Recursive Locking and Its Implications in Go

Previous Article: Avoiding Starvation in Concurrent Systems with Go

Series: Concurrency and Synchronization in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant