Patterns similar to this reader code are common: the first thread into a
section locks a semaphore (or queues) and the last one out unlocks it.
The name of the pattern is Lightswitch, by analogy with the pattern where
the first person into a room turns on the light (locks the mutex) and the last one
out turns it off (unlocks the mutex).
Here is a class definition for a Lightswitch:
1 class Lightswitch : 2 def __init__ ( self ): 3 self . counter = 0 4 self . mutex = Semaphore (1) 5 6 def lock ( self , semaphore ): 7 self . mutex . wait () 8 self . counter += 1 9 if self . counter == 1: 10 semaphore . wait () 11 self . mutex . signal () 12 13 def unlock ( self , semaphore ): 14 self . mutex . wait () 15 self . counter -= 1 16 if self . counter == 0: 17 semaphore . signal () 18 self . mutex . signal ()
lock takes one parameter, a semaphore that it will check and possibly hold.
If the semaphore is locked, the calling thread blocks on semaphore and all
subsequent threads block on self.mutex. When the semaphore is unlocked,
the first waiting thread locks it again and all waiting threads proceed.
If the semaphore is initially unlocked, the first thread locks it and all subsequent
unlock has no effect until every thread that called lock also calls unlock.
When the last thread calls unlock, it unlocks the semaphore
Using these functions, we can rewrite the reader code more simply:
1 readLightswitch = Lightswitch () 2 roomEmpty = Semaphore (1)
readLightswitch is a shared Lightswitch object whose counter is initially
Readers-writers solution (reader)
1 readLightswitch . lock ( roomEmpty ) 2 # critical section 3 readLightswitch . unlock ( roomEmpty )
For more concurrency patterns written in python: