Sling Academy
Home/Golang/Avoiding Starvation in Concurrent Systems with Go

Avoiding Starvation in Concurrent Systems with Go

Last updated: November 27, 2024

When developing concurrent systems, a common issue that programmers encounter is starvation. Starvation occurs when a concurrency control scheme designed to handle concurrent access to shared resources ends up excessively delaying or even completely denying service to some requests. In this article, we will explore how you can avoid starvation problems when writing concurrent programs in the Go programming language.

Understanding Starvation

Starvation in concurrent programming arises when certain processes or threads are perpetually denied the resources they require to proceed. Typical scenarios include a thread that waits indefinitely due to higher priority threads consuming the available computing resources.

Identifying Starvation in Go

In Go, starvation can occur in various constructs like goroutines, channels, and for loops. Consider the example:

package main

import (
	"fmt"
	"time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
	for j := range jobs {
		fmt.Println("Worker", id, "started job", j)
		time.Sleep(time.Second)
		fmt.Println("Worker", id, "finished job", j)
		results <- j * 2
	}
}

func main() {
	const numJobs = 5
	jobs := make(chan int, numJobs)
	results := make(chan int, numJobs)

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

	for j := 1; j <= numJobs; j++ {
		jobs <- j
	}
	close(jobs)

	for r := 1; r <= numJobs; r++ {
		<-results
	}
}

In this implementation, workers are created to process jobs. If a balance isn't maintained between the generation and processing of jobs, some workers could starve while others are pre-occupied.

Strategies to Avoid Starvation

Let's discuss some techniques to prevent starvation using Go:

Use of Go Scheduler

Rely on Go's powerful runtime scheduler which balances workload efficiently. It's designed to preempt goroutine execution and prevent one from blocking others indefinitely.

Yielding Timeslices

Strategically invoke time functions that can yield processor time thus allowing blocked or waiting goroutines a chance to execute:

package main

import (
	"fmt"
	"time"
)

func main() {
	for i := 1; i <= 10; i++ {
		go func(i int) {
			time.Sleep(time.Millisecond * 100)
			fmt.Println("Job", i)
		}(i)
	}

	time.Sleep(time.Second)
}

In the example, sleep calls are deliberately added to allow time for other goroutines to proceed with their tasks across available processors.

Priority Scheduling

Utilize custom scheduling algorithms leveraging channels or semaphores to hand-off priority jobs to available resources:

// Priority can be managed via separate queues (channels) for high and low priority tasks.
// Priority handling with select types on channels. Here’s a sample indicating priority via separate channels.
package main

import (
	"fmt"
)

func priorityWorker(priorityJobs <-chan int, regularJobs <-chan int) {
	for {
		select {
		case j := <-priorityJobs:
			fmt.Println("Processing priority job", j)
		case j := <-regularJobs:
			fmt.Println("Processing regular job", j)
		}
	}
}

func main() {
	priorityJobs := make(chan int, 5)
	regularJobs := make(chan int, 5)

	go priorityWorker(priorityJobs, regularJobs)

	for i := 1; i <= 5; i++ {
		priorityJobs <- i
		regularJobs <- i
	}
}

The use of select in workers allows Go programs to prioritize or pick from multiple operations judiciously.

Conclusion

Managing concurrent operations to avoid starvation requires excellent understanding and well-thought-out designs. Proper utilization of Go's scheduler features, implementing appropriate time phasing, or establishing job priority can prevent such issues effectively.

Next Article: Implementing Thread Pools with Goroutines

Previous Article: Creating Non-Blocking Channel Operations in 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