Read-write lock with only one underlying lock?

前端 未结 2 1200
春和景丽
春和景丽 2021-01-16 12:07

I\'ve written a read-write lock using Python\'s concurrency primitives (I think!). Every implementation I\'ve read on SO or elsewhere seems to use 2 locks -- one for reads,

相关标签:
2条回答
  • 2021-01-16 12:45

    You are not using a single lock.
    You are using a lock and a condition variable

    self.read_lock = t.Condition(t.Lock())
    

    A condition variable is a concurrency primitive too. A more complex one than a lock.

    note : please do not call a condition variable object read_lock

    edit: Your code seems correct to me, as it solves the First readers-writers problem. As you said it may starve writer. This is not a small issue. The logic behind reader writer is that there may be a lot more reads than writes
    An additional lock allow to solve the Second readers-writers problem, where a writer doesn't starve. Indeed, readers have to wait when there is a writer waiting for the resource.

    0 讨论(0)
  • 2021-01-16 12:50

    One more solution using a lock and a condition. Takes care of the starvation issue and also supports promotion of a read lock to write lock when requested from the same thread.

    # From O'Reilly Python Cookbook by David Ascher, Alex Martelli
    # With changes to cover the starvation situation where a continuous
    #   stream of readers may starve a writer, Lock Promotion and Context Managers
    
    class ReadWriteLock:
      """ A lock object that allows many simultaneous "read locks", but
      only one "write lock." """
    
      def __init__(self, withPromotion=False):
        self._read_ready = threading.Condition(threading.RLock(  ))
        self._readers = 0
        self._writers = 0
        self._promote = withPromotion
        self._readerList = []  # List of Reader thread IDs
        self._writerList = []  # List of Writer thread IDs
    
      def acquire_read(self):
        logging.debug("RWL : acquire_read()")
        """ Acquire a read lock. Blocks only if a thread has
        acquired the write lock. """
        self._read_ready.acquire(  )
        try:
          while self._writers > 0:
            self._read_ready.wait()
          self._readers += 1
        finally:
          self._readerList.append(threading.get_ident())
          self._read_ready.release(  )
    
      def release_read(self):
        logging.debug("RWL : release_read()")
        """ Release a read lock. """
        self._read_ready.acquire(  )
        try:
          self._readers -= 1
          if not self._readers:
            self._read_ready.notifyAll(  )
        finally:
          self._readerList.remove(threading.get_ident())
          self._read_ready.release(  )
    
      def acquire_write(self):
        logging.debug("RWL : acquire_write()")
        """ Acquire a write lock. Blocks until there are no
        acquired read or write locks. """
        self._read_ready.acquire(  )   # A re-entrant lock lets a thread re-acquire the lock
        self._writers += 1
        self._writerList.append(threading.get_ident())
        while self._readers > 0:
          # promote to write lock, only if all the readers are trying to promote to writer
          # If there are other reader threads, then wait till they complete reading
          if self._promote and threading.get_ident() in self._readerList and set(self._readerList).issubset(set(self._writerList)):
            break
          else:
            self._read_ready.wait(  )
    
      def release_write(self):
        logging.debug("RWL : release_write()")
        """ Release a write lock. """
        self._writers -= 1
        self._writerList.remove(threading.get_ident())
        self._read_ready.notifyAll(  )
        self._read_ready.release(  )
    
    #----------------------------------------------------------------------------------------------------------
    
    class ReadRWLock:
      # Context Manager class for ReadWriteLock
      def __init__(self, rwLock):
        self.rwLock = rwLock
    
      def __enter__(self):
        self.rwLock.acquire_read()
        return self         # Not mandatory, but returning to be safe
    
      def __exit__(self, exc_type, exc_value, traceback):
        self.rwLock.release_read()
        return False        # Raise the exception, if exited due to an exception
    
    #----------------------------------------------------------------------------------------------------------
    
    class WriteRWLock:
      # Context Manager class for ReadWriteLock
      def __init__(self, rwLock):
        self.rwLock = rwLock
    
      def __enter__(self):
        self.rwLock.acquire_write()
        return self         # Not mandatory, but returning to be safe
    
      def __exit__(self, exc_type, exc_value, traceback):
        self.rwLock.release_write()
        return False        # Raise the exception, if exited due to an exception
    
    #----------------------------------------------------------------------------------------------------------
    
    0 讨论(0)
提交回复
热议问题