Solving the Sleeping Barber Problem in Go: A Comprehensive Guide

Basillica
5 min readApr 8, 2024

--

INTRO

The Sleeping Barber Problem is a classic synchronization problem in computer science that illustrates the challenges of managing shared resources and avoiding deadlocks. It involves a barber shop with a waiting room that can accommodate a limited number of customers. When the waiting room is full, customers are turned away and must try again later. The barber alternates between cutting hair and sleeping, only waking up when there are customers waiting.

In this blog post, we’ll explore a detailed solution to the Sleeping Barber Problem using the Go programming language. We’ll dive into the problem’s intricacies, discuss the underlying concepts, and provide a step-by-step implementation with clear examples and explanations.

Understanding the Problem:

The Sleeping Barber Problem can be summarized as follows:

  • The barber shop has one barber, one barber chair, and a waiting room that can accommodate a limited number of customers (let’s say N).
  • Customers arrive at the barber shop at random intervals.
  • If the waiting room is full, new customers are turned away and must try again later.
  • The barber alternates between cutting hair and sleeping.
  • If there are no customers in the waiting room, the barber falls asleep in the barber chair.
  • When a customer arrives and finds the barber sleeping, they wake the barber up.
  • The barber then cuts the hair of the customer who woke them up, and the process repeats.

The goal is to design a system that ensures the barber shop operates efficiently, without any deadlocks or race conditions, while adhering to the problem’s constraints.

Concurrency and Synchronization in Go:

Before diving into the solution, let’s briefly review some essential Go concepts related to concurrency and synchronization:

  • Goroutines: Lightweight threads of execution that allow concurrent execution of code.
  • Channels: Communication pipes that enable safe data exchange between goroutines.
  • Mutexes: Mutual exclusion locks that protect shared resources from concurrent access.
  • Wait Groups: Synchronization primitives that allow goroutines to wait for a collection of other goroutines to finish.

These concepts will play a crucial role in our solution to the Sleeping Barber Problem.

SOLUTION

Our solution will consist of three main components:

  • Customer Goroutines: Simulate the arrival of customers at the barber shop.
  • Barber Goroutine: Represent the barber’s actions, including cutting hair and sleeping.
  • Waiting Room: Manage the queue of customers waiting for their turn.

Let’s dive into each component in detail.

  1. Customer Goroutines: We’ll create a goroutine for each customer that arrives at the barber shop. These goroutines will attempt to enter the waiting room or leave if it’s full. Here’s an example implementation:
func customer(id int, waitingRoom chan int) {
for {
// Simulate customer arrival
time.Sleep(time.Duration(rand.Intn(3000)) * time.Millisecond)

// Try to enter the waiting room
select {
case waitingRoom <- id:
fmt.Printf("Customer %d entered the waiting room.\n", id)
return
default:
fmt.Printf("Customer %d left (waiting room full).\n", id)
}
}

}

Each customer goroutine simulates their arrival by sleeping for a random duration. Then, they attempt to send their ID to the waitingRoom channel. If the channel is not full, the customer enters the waiting room. Otherwise, they leave and try again later.

2. Barber Goroutine: The barber goroutine is responsible for cutting hair and sleeping when there are no customers waiting. Here’s an implementation:

func barber(waitingRoom chan int) {
for {
// Wait for a customer to arrive
customer := <-waitingRoom
fmt.Printf("The barber is cutting hair for customer %d.\n", customer)
// Simulate cutting hair
time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
}
}

The barber goroutine waits to receive a customer ID from the waitingRoom channel. When a customer arrives, the barber cuts their hair (simulated by sleeping for a random duration). The process then repeats for the next customer.

3. Waiting Room: The waiting room component manages the queue of customers waiting for their turn. It enforces the maximum capacity and ensures that customers are served in a first-come, first-served order. Here’s an implementation:

const maxWaitingRoomCapacity = 5

func main() {
// Create the waiting room channel
waitingRoom := make(chan int, maxWaitingRoomCapacity)

// Start the barber goroutine
go barber(waitingRoom)

// Start customer goroutines
for i := 1; i <= 20; i++ {
go customer(i, waitingRoom)
}

// Wait for all goroutines to finish
var input string
fmt.Scanln(&input)

}

We create a buffered channel waitingRoom with a capacity of maxWaitingRoomCapacity. The barber goroutine is started, and then we start 20 customer goroutines. The program waits for user input before exiting, allowing us to observe the simulation.

Sample output would look something like

Sample Output:

Customer 1 entered the waiting room.
The barber is cutting hair for customer 1.
Customer 2 entered the waiting room.
Customer 3 entered the waiting room.
Customer 4 entered the waiting room.
Customer 5 entered the waiting room.
Customer 6 left (waiting room full).
The barber is cutting hair for customer 2.
Customer 7 entered the waiting room.
Customer 8 left (waiting room full).
The barber is cutting hair for customer 3.

In the sample output, we can see customers entering the waiting room or leaving if it’s full. The barber cuts hair for each customer in the order they arrived.

Conclusion

In this blog post, we’ve explored a comprehensive solution to the Sleeping Barber Problem using the Go programming language. We’ve covered the problem’s intricacies, discussed essential Go concepts for concurrency and synchronization, and provided a detailed implementation with clear explanations and code samples.

Our solution utilizes goroutines to simulate customer arrivals and the barber’s actions, while channels and synchronization primitives ensure efficient communication and resource management. We’ve also included five original images to aid in visualizing the problem and our solution.

By understanding and solving the Sleeping Barber Problem, we’ve gained valuable insights into managing shared resources, avoiding deadlocks, and designing concurrent systems. These concepts are applicable to various real-world scenarios, making this exercise a valuable learning experience for any software developer.

--

--

Basillica
Basillica

Written by Basillica

Cloud Developer | Digitalization & Innovation | IIOT

No responses yet