Python读写锁实现实现代码解析
作者:-零 发布时间:2023-06-16 23:27:12
起步
Python 提供的多线程模型中并没有提供读写锁,读写锁相对于单纯的互斥锁,适用性更高,可以多个线程同时占用读模式的读写锁,但是只能一个线程占用写模式的读写锁。
通俗点说就是当没有写锁时,就可以加读锁且任意线程可以同时加;而写锁只能有一个线程,且必须在没有读锁时才能加上。
简单的实现
import threading
class RWlock(object):
def __init__(self):
self._lock = threading.Lock()
self._extra = threading.Lock()
self.read_num = 0
def read_acquire(self):
with self._extra:
self.read_num += 1
if self.read_num == 1:
self._lock.acquire()
def read_release(self):
with self._extra:
self.read_num -= 1
if self.read_num == 0:
self._lock.release()
def write_acquire(self):
self._lock.acquire()
def write_release(self):
self._lock.release()
这是读写锁的一个简单的实现,self.read_num 用来保存获得读锁的线程数,这个属性属于临界区,对其操作也要加锁,所以这里需要一个保护内部数据的额外的锁 self._extra 。
但是这个锁是不公平的。理想情况下,线程获得所的机会应该是一样的,不管线程是读操作还是写操作。而从上述代码可以看到,读请求都会立即设置 self.read_num += 1,不管有没有获得锁,而写请求想要获得锁还得等待 read_num 为 0 。
所以这个就造成了只有锁没有被占用或者没有读请求时,可以获得写权限。我们应该想办法避免读模式锁长期占用。
读写锁的优先级
读写锁也有分 读优先 和 写优先。上面的代码就属于读优先。
如果要改成写优先,那就换成去记录写线程的引用计数,读和写在同时竞争时,可以让写线程增加写的计数,这样可使读线程的读锁一直获取不到, 因为读线程要先判断写的引用计数,若不为0,则等待其为 0,然后进行读。这部分代码不罗列了。
但这样显然不够灵活。我们不需要两个相似的读写锁类。我们希望重构我们代码,使它更强大。
改进
为了能够满足自定义优先级的读写锁,要记录等待的读写线程数,并且需要两个条件 threading.Condition 用来处理哪方优先的通知。计数引用可以扩大语义:正数:表示正在读操作的线程数,负数:表示正在写操作的线程数(最多-1)
在获取读操作时,先然后判断时候有等待的写线程,没有,进行读操作,有,则等待读的计数加 1 后等待 Condition 通知;等待读的计数减 1,计数引用加 1,继续读操作,若条件不成立,循环等待;
在获取写操作时,若锁没有被占用,引用计数减 1,若被占用,等待写线程数加 1,等待写条件 Condition 的通知。
读模式和写模式的释放都是一样,需要根据判断去通知对应的 Condition:
class RWLock(object):
def __init__(self):
self.lock = threading.Lock()
self.rcond = threading.Condition(self.lock)
self.wcond = threading.Condition(self.lock)
self.read_waiter = 0 # 等待获取读锁的线程数
self.write_waiter = 0 # 等待获取写锁的线程数
self.state = 0 # 正数:表示正在读操作的线程数 负数:表示正在写操作的线程数(最多-1)
self.owners = [] # 正在操作的线程id集合
self.write_first = True # 默认写优先,False表示读优先
def write_acquire(self, blocking=True):
# 获取写锁只有当
me = threading.get_ident()
with self.lock:
while not self._write_acquire(me):
if not blocking:
return False
self.write_waiter += 1
self.wcond.wait()
self.write_waiter -= 1
return True
def _write_acquire(self, me):
# 获取写锁只有当锁没人占用,或者当前线程已经占用
if self.state == 0 or (self.state < 0 and me in self.owners):
self.state -= 1
self.owners.append(me)
return True
if self.state > 0 and me in self.owners:
raise RuntimeError('cannot recursively wrlock a rdlocked lock')
return False
def read_acquire(self, blocking=True):
me = threading.get_ident()
with self.lock:
while not self._read_acquire(me):
if not blocking:
return False
self.read_waiter += 1
self.rcond.wait()
self.read_waiter -= 1
return True
def _read_acquire(self, me):
if self.state < 0:
# 如果锁被写锁占用
return False
if not self.write_waiter:
ok = True
else:
ok = me in self.owners
if ok or not self.write_first:
self.state += 1
self.owners.append(me)
return True
return False
def unlock(self):
me = threading.get_ident()
with self.lock:
try:
self.owners.remove(me)
except ValueError:
raise RuntimeError('cannot release un-acquired lock')
if self.state > 0:
self.state -= 1
else:
self.state += 1
if not self.state:
if self.write_waiter and self.write_first: # 如果有写操作在等待(默认写优先)
self.wcond.notify()
elif self.read_waiter:
self.rcond.notify_all()
elif self.write_waiter:
self.wcond.notify()
read_release = unlock
write_release = unlock
来源:https://www.cnblogs.com/-wenli/p/13618333.html
猜你喜欢
- 在新建数据库或附加数据库后,想添加关系表,结果出现下面的错误: 此数据库没有有效所有者,因此无法安装数据库关系图支持对象。若要继续,请首先使
- Js代码:/*** 验证码*/function yzm(){var codeChars = new Array(0, 1, 2, 3, 4,
- 本文实例讲述了python避免死锁方法。分享给大家供大家参考。具体分析如下:当两个或者更多的线程在等待资源的时候就会产生死锁,两个线程相互等
- 在国外一博客看到的技巧,终于解决IE的这个老大难问题。我在IE的setAttribute bug也提到其解决方法,一是innerHTML,一
- SQL Server通常都运行在多处理器的服务器上,这一点在现在尤为普遍。原因是多内核的处理器越来越普及。那么,在多处理器环境下,Windo
- 1.pytnon的基本数据类型数字类型:整型(int)和浮点型(float)字符串(str)类型:用引号(单双引号都可以)定义一个字符串布尔
- 常用配置以下配置能使用File -> New Projects Settings -> Settings for New Pro
- 大家好,今天分享一个实用的办公脚本:将多个PDF合并为一个PDF,例如我手上现在有如下3个PDF分册,需要整合成一个完整的PDF如果换成你操
- 引言在负责咨询工作的过去 6 年中,我曾多次听说关于数据访问和操作方面的问题,它时刻困扰着用户:“如何编写应用程序,以便
- 前言许多任务程序如果为其构造为一个命令行界面,就可以通过接受不同的参数来改变它的工作方式。例如,在爬虫程序中,不同 URL&nbs
- 方案:◆1、SELECT TOP PAGESIZE NEWSTITLEFORM NEWSINFO WHERE NEWSID NOT IN(S
- 问:怎样才能指定MySQL只监听某个特定地址?答:比较常见的办法是,在my.cnf之mysqld节,添加bind-address=127.0
- 本文实例讲述了php延迟静态绑定的方法。分享给大家供大家参考。具体分析如下:php延迟静态绑定:指类的self,不是以定义时为准,而是以计算
- 也许自己真的就是有手残的毛病,你说好端端的环境配置好了,自己还在那里瞎鼓捣,我最不想看到的就是在安装一个别的模块的时候,自动卸载了本地的其他
- 本文实例讲述了python中管道用法。分享给大家供大家参考。具体如下:#!coding=utf-8import multiprocessin
- google 的设计原则中文1.易用性-聚焦在人,方便他们的生活,工作,梦想。2.速度-分秒必争3.简单-简单而强有力4.关联性- 对初学者
- 用ASP编写网站应用程序时间长了,难免会遇到各式各样的问题,其中关于如何上传文件到服务器恐怕是遇见最多的问题了,尤其是上传图片,比如你想要在
- 请按步骤进行,未进行前面的步骤时,请不要做后面的步骤,以免损坏你的数据库.一般不建议做第4,6两步,第4步不安全,有可能损坏数据库或丢失数据
- golang.org/x包放到了https://github.com/golang/text中,下载时需要先在本地建立golang.org/
- 本文实例为大家分享了wxPython分隔窗口的具体代码,供大家参考,具体内容如下1、分割窗口分隔窗口(wx.SplitterWindow)就