Skip to content

Carelyte/reader-writer-problem

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 

Repository files navigation

Readers-Writers Problem - Explained with Python Implementation

Introduction

The Readers-Writers Problem is a classic synchronization problem in concurrent programming. It involves multiple readers and writers who need to access a shared resource (such as a database or file) while avoiding conflicts.

Problem Statement

  • Multiple readers can read the shared resource simultaneously.
  • Only one writer can write at a time, and no readers should be reading while a writer is writing.
  • If a writer is writing, all readers must wait.

Solution Approach

We solve this problem using semaphores and mutex locks to manage access to the shared resource.

Code Breakdown

1. Importing Required Modules

import threading
import time
import random
  • threading: Used for creating multiple threads (readers and writers).
  • time: Simulates delays in reading and writing.
  • random: Introduces randomness in execution to mimic real-world scenarios.

2. Declaring Shared Resources

# Semaphore to control writer access (ensures only one writer at a time)
writer_semaphore = threading.Semaphore(1)

# Mutex (lock) to manage the reader count safely
reader_mutex = threading.Lock()

# Shared resource (simulated database or file) and the count of active readers
shared_data = "Initial Data"
reader_count = 0
  • writer_semaphore: Ensures only one writer can write at a time.
  • reader_mutex: Protects the reader_count variable when multiple readers access it.
  • shared_data: Represents the common resource being read and written.
  • reader_count: Tracks the number of active readers.

3. Reader Function

def reader(reader_id):
    global reader_count
    
    while True:
        time.sleep(random.randint(1, 3))  # Simulate random reading intervals
        
        # Entry section: Safely update the reader count
        reader_mutex.acquire()
        reader_count += 1
        if reader_count == 1:
            writer_semaphore.acquire()  # First reader locks the writer out
            print("First reader has started reading, blocking writers.")
        reader_mutex.release()
        
        # Critical section: Reading the shared resource
        print(f"Reader {reader_id} is reading: {shared_data}")
        time.sleep(random.uniform(0.5, 2))  # Simulate time spent reading
        
        # Exit section: Safely update the reader count
        reader_mutex.acquire()
        reader_count -= 1
        if reader_count == 0:
            writer_semaphore.release()  # Last reader unlocks the writer
            print("Last reader has finished reading, allowing writers.")
        reader_mutex.release()

Explanation:

  • Each reader starts after a random delay.
  • When the first reader starts reading, it blocks writers using writer_semaphore.acquire().
  • Readers continue reading in parallel.
  • When the last reader finishes, it unlocks the writer, allowing writing to resume.

4. Writer Function

def writer(writer_id):
    global shared_data
    
    while True:
        time.sleep(random.randint(2, 5))  # Simulate random writing intervals
        
        # Entry section: Gain exclusive access to write
        writer_semaphore.acquire()
        print(f"Writer {writer_id} has started writing.")
        
        # Critical section: Updating the shared resource
        shared_data = f"Updated by Writer {writer_id}"
        time.sleep(random.uniform(1, 3))  # Simulate writing time
        
        # Exit section: Release the lock for other writers/readers
        print(f"Writer {writer_id} has finished writing.")
        writer_semaphore.release()

Explanation:

  • Writers enter one by one, enforced by writer_semaphore.acquire().
  • They update the shared_data while blocking all readers and writers.
  • After writing, they release the lock, allowing readers or another writer to proceed.

5. Creating and Starting Threads

# Number of reader and writer threads to create
num_readers = 3
num_writers = 2

# Creating reader and writer threads
reader_threads = [threading.Thread(target=reader, args=(i,)) for i in range(num_readers)]
writer_threads = [threading.Thread(target=writer, args=(i,)) for i in range(num_writers)]

# Starting all threads
for t in reader_threads + writer_threads:
    t.start()

# Joining all threads (optional, since they run indefinitely)
for t in reader_threads + writer_threads:
    t.join()

Explanation:

  • We create 3 readers and 2 writers.
  • Each reader and writer is assigned a thread and started.
  • The .join() ensures threads run indefinitely (can be removed for continuous execution).

Expected Output

The output will vary due to randomness, but it will follow these patterns:

First reader has started reading, blocking writers.
Reader 0 is reading: Initial Data
Reader 1 is reading: Initial Data
Last reader has finished reading, allowing writers.
Writer 0 has started writing.
Writer 0 has finished writing.
First reader has started reading, blocking writers.
Reader 2 is reading: Updated by Writer 0
Last reader has finished reading, allowing writers.
Writer 1 has started writing.
Writer 1 has finished writing.

Key Takeaways

  • Multiple readers can read simultaneously.
  • Only one writer can write at a time.
  • Readers block writers but not each other.
  • The first reader locks out writers, and the last reader unlocks them.

This ensures efficient resource access while maintaining consistency. 🚀

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages