只有一个底层锁的读写锁?
Posted
技术标签:
【中文标题】只有一个底层锁的读写锁?【英文标题】:Read-write lock with only one underlying lock? 【发布时间】:2016-08-09 23:41:26 【问题描述】:我使用 Python 的并发原语编写了一个读写锁(我想!)。我在 SO 或其他地方阅读的每个实现似乎都使用 2 个锁——一个用于读取,另一个用于写入。我的实现只包含一个用于读取的监视器,但我可能遗漏了一些重要的东西——任何人都可以确认这会起作用吗?如果是这样,使用额外的写锁有什么好处?
这是经典的读写锁,读者偏好(可能会饿死作者)。我使用一个虚拟缓存来演示读写。
import threading as t
class ReadWriteCache(object):
def __init__(self):
self.cache =
self.reads = 0
self.read_cond = t.Condition(t.Lock())
def read(self, key):
with self.read_cond: # Register the read, so writes will wait()
self.reads += 1
result = self.cache[key]
with self.read_cond:
self.reads -= 1
if not self.reads:
self.read_cond.notify_all()
return result
def update(self, key, value):
with self.read_cond:
while self.reads:
self.read_cond.wait() # Wait for reads to clear
self.cache[key] = value # With read lock, update value
【问题讨论】:
在哪里可以找到双锁版本的示例? @ErikR,你可以在这里找到一些例子: @ErikR,输入太早了。请参阅下面的问题。接受的答案中的每个链接,以及 pasztorpisti 的代码答案。它们要么使用 2 个锁,要么具有某种双事件系统,在此系统中,写入者和读取者都在完成时相互发出信号。 ***.com/questions/16261902/… 【参考方案1】:您没有使用单个锁。 您正在使用锁和条件变量
self.read_lock = t.Condition(t.Lock())
条件变量也是一种并发原语。比锁更复杂的。
注意:请不要调用条件变量对象read_lock
编辑: 您的代码对我来说似乎是正确的,因为它解决了First readers-writers problem。正如你所说,它可能会饿死作家。这不是一个小问题。读写器背后的逻辑是读取可能比写入多得多 额外的锁可以解决第二个读者-作者问题,即作者不会饿死。事实上,当有作者在等待资源时,读者必须等待。
【讨论】:
啊,这很有道理——我想我实际上是在同时使用锁和事件。我已重命名对象,是否需要重新审视正确性?【参考方案2】:使用锁和条件的另一种解决方案。处理饥饿问题,还支持在从同一线程请求时将读锁提升为写锁。
# 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
#----------------------------------------------------------------------------------------------------------
【讨论】:
以上是关于只有一个底层锁的读写锁?的主要内容,如果未能解决你的问题,请参考以下文章
QReadWriteLock读写锁的一点测试(它是逻辑锁,并没有与实物相联系),只有锁住了读,才允许再次读,否则一概不允许