4.3.3. Readers and Writers

If one notebook exists where writers may write information to, only one writer may write at a time. Confusion may arise if a reader is trying read at the same as a writer is writing. Since readers only look at the data, but do not modify the data, we can allow more than one reader to read at the same time.

The main complexity with this problems stems from allowing more than one reader to access the data at the same time.

../_images/readers-writers.png

Writers have mutual exclusion, but multiple readers at the same time is allowed.

Note

Readers and Writers is a similar problem to the One Lane Bridge problem. Like Readers, multiple instances of cars going in the same direction is allowed on a one lane bridge. The number of cars on the bridge may be limited and cars going in opposite direction are not allowed.

4.3.3.1. Simple Lock Solution

Here is a basic Python class that implements a solution to Readers and Writers using simple locks:

import threading

class Reader_writer(Object):
    "Reader - writer solution using simple locks"
    def __init__(self):
        self.readers = 0
        self.writers = 0
        self.mutex1 = threading.Lock()
        self.mutex2 = threading.Lock()
        self.readPending = threading.Lock()
        self.writeBlock = threading.Lock()
        self.readBlock = threading.Lock()

# This is kind of complicated locking stuff. Don't worry about why there
# are so many locks, it is just part of the known solution to
# the readers and writers problem.
#
# The basic idea of the readers and writers algorithm is to use locks to
# quickly see how many other readers and writers there are.  writeBlock
# is the only lock held while reading the data.  Thus the reader only
# prevents writers from entering the critical section.  Both writeBlock
# and readBlock are held while writing to the data.  Thus the writer
# blocks readers and other writers.

# Note: This shows reader() and writer() methods.  The critical section
#   code could be added directly to these methods.
#   Antother valid way to structure the code is to make methods for
#   start_read(), end_read(), start_write(), end_write(). Other
#   methods for read(), write() could be added to the class; or, the
#   threads that use the class could read and write extrenal to the
#   class.

    def reader(self, lastread):
        "Reader of readers and writers algorithm"
        self.readPending.acquire()
        self.readBlock.acquire()
        self.mutex1.acquire()
        self.readers = self.readers + 1
        if self.readers == 1: self.writeBlock.acquire()
        self.mutex1.release()
        self.readBlock.release()
        self.readPending.release()
        #--------------------------
        #
        # critical section
        #
        #--------------------------
        self.mutex1.acquire()
        self.readers = self.readers - 1
        if self.readers == 0: self.writeBlock.release()
        self.mutex1.release()
        return retVal

    def writer(self, data):
        "Writer of readers and writers algorithm"
        self.mutex2.acquire()
        self.writers = self.writers + 1
        if self.writers == 1: self.readBlock.acquire()
        self.mutex2.release()
        self.writeBlock.acquire()
        #--------------------------
        #
        # critical section
        #
        #--------------------------
        self.writeBlock.release()
        self.mutex2.acquire()
        self.writers = self.writers - 1
        if self.writers == 0: self.readBlock.release()
        self.mutex2.release()

4.3.3.2. Monitor Solution

Here is a basic Python class that implements a solution to the Readers and Writers problem with monitors. It is a little more complicated than most monitor based problems because it needs two monitors and one mutual exclusion lock, but is probably simpler than the lock based solution. Monitors are needed to regulate the start of both reading and writing. In addition, a lock is needed to protect the updating of shared variables. Use of the lock is the same as with the Shared Buffer Problem.

import threading

class Reader_writer(Object):
    "Reader - writer solution using two monitors"
    def __init__(self):
        self.readers = 0
        self.busy = False  # writer test
        self.OKtoRead = threading.Condition()
        self.OKtoWrite = threading.Condition()
        self.mutex = threading.Lock()

    def reader(self, lastread):
        "Reader of readers and writers algorithm"
        self.OKtoRead.acquire()
        self.mutex.acquire()
        while self.busy:
            self.mutex.release()
            self.OKtoRead.wait()
            self.mutex.acquire()
        self.readers = self.readers + 1
        self.mutex.release()
        self.OKtoRead.notify()
        self.OKtoRead.release()
        # Once one reader can start, they all can
        #--------------------------
        #
        # critical section
        #
        #--------------------------
        self.OKtoRead.acquire()
        self.mutex.acquire()
        self.readers = self.readers - 1
        if self.readers == 0:
            self.OKtoWrite.acquire()
            self.OKtoWrite.notify()
            self.OKtoWrite.release()
        self.mutex.release()
        self.OKtoRead.release()
        return retVal

    def writer(self, data):
        "Writer of readers and writers algorithm"
        self.OKtoWrite.acquire()
        self.mutex.acquire()
        while self.busy or self.readers > 0:
            self.mutex.release()
            self.OKtoWrite.wait()
            self.mutex.acquire()
        self.busy = True
        self.mutex.release()
        self.OKtoWrite.release()
        #--------------------------
        #
        # critical section
        #
        #--------------------------
        self.OKtoWrite.acquire()
        self.OKtoRead.acquire()
        self.mutex.acquire()
        self.busy = False
        self.mutex.release()
        self.OKtoWrite.notify()
        self.OKtoWrite.release()
        self.OKtoRead.notify()
        self.OKtoRead.release()