# CS 450 S14; Reader-Writer Solution # version 2014-02-12 3:03 PM # # Sample program for Reader-Writer Problem # # Writer n prints (wn wn wn) in its critical section # # If Reader n needs to get and release the DB, it prints # (rn RC [rn DB rn RC) rn (rn RC rn DB] rn RC) # # The two (rn RC rn RC) are for the reader count mutex # get/release. If Reader n doesn't need to get the DB # mutex, it doesn't print the [rn DB; similar for rn DB] # and releasing the DB. # # If safe is true, we use a mutex semaphore for the # reader count manipulation and a mutex semaphore for # database acquisition. # from threading import Thread, BoundedSemaphore import random, time # main program creates two readers and two writers and runs # them in parallel # def main(safe = True, trials = 5): global RC_mutex, DB_mutex, read_count read_count = 0; # nbr readers RC_mutex = BoundedSemaphore(1); DB_mutex = BoundedSemaphore(1); p_args = {'safe': safe, 'trials': trials} r1 = Thread(target = reader, args=(1,), kwargs=p_args) r2 = Thread(target = reader, args=(2,), kwargs=p_args) w1 = Thread(target = writer, args=(1,), kwargs=p_args) w2 = Thread(target = writer, args=(2,), kwargs=p_args) [r1.start(), r2.start(), w1.start(), w2.start()] [r1.join(), r2.join(), w1.join(), w2.join()] print() # A writer repeatedly gets the DB, writes the DB, and then # releases the DB # def writer(id, safe = True, trials = 5): global DB_mutex for _ in range(trials): if safe: DB_mutex.acquire() trace_write(id, '(w{}') trace_write(id, 'w{}') # "Write" the DB trace_write(id, 'w{})') if safe: DB_mutex.release() time.sleep(random.randint(0,2)) # Utility for printing writer trace info and # introducing some random sleep (to simulate # nondeterministic speed of execution) # def trace_write(id, tag): print((tag + ' ').format(id), flush=True, end='') time.sleep(random.randint(0,2)) # A reader increments the read count, reads the DB, then # decrements the read count. If it increments the read # count to 1, it gets the DB on behalf of all the readers. # If it decrements the read count to 0, it releases the # DB so that a waiting writer (if any) can get the DB. # def reader(id, safe = True, trials = 5): global RC_mutex, DB_mutex, read_count for _ in range(trials): if safe: RC_mutex.acquire() trace_read(id, '(r{} RC', end='') read_count = read_count + 1 if safe: if read_count == 1: DB_mutex.acquire() trace_read(id, '[r{} DB') RC_mutex.release() trace_read(id, 'r{} RC)') trace_read(id, 'r{}') # "Read" the DB if safe: RC_mutex.acquire() trace_read(id, '(r{} RC', end='') read_count = read_count - 1 if safe: if read_count == 0: DB_mutex.release() trace_read(id, 'r{} DB]') RC_mutex.release() trace_read(id, 'r{} RC)') time.sleep(random.randint(0,2)) # Utility for printing reader trace info and # introducing some random sleep (to simulate # nondeterministic speed of execution). If # end is the empty string, we print a space # instead of a carriage return. # def trace_read(id, tag, end='\n'): print((tag + (' ' if end == '' else '')).\ format(id), flush=True, end=end ) time.sleep(random.randint(0,2))