Python读写锁实现实现代码解析


Posted in Python onNovember 28, 2020

起步

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
一个计算身份证号码校验位的Python小程序
Aug 15 Python
使用Python脚本来控制Windows Azure的简单教程
Apr 16 Python
python3编码问题汇总
Sep 06 Python
Python中将字典转换为列表的方法
Sep 21 Python
python executemany的使用及注意事项
Mar 13 Python
python学习入门细节知识点
Mar 29 Python
Pandas 数据框增、删、改、查、去重、抽样基本操作方法
Apr 12 Python
Python 实现微信防撤回功能
Apr 29 Python
python的内存管理和垃圾回收机制详解
May 18 Python
python实现一个猜拳游戏
Apr 05 Python
Tensorflow中批量读取数据的案列分析及TFRecord文件的打包与读取
Jun 30 Python
详解BeautifulSoup获取特定标签下内容的方法
Dec 07 Python
python解压zip包中文乱码解决方法
Nov 27 #Python
python 实现控制鼠标键盘
Nov 27 #Python
Python模拟键盘输入自动登录TGP
Nov 27 #Python
python操作toml文件的示例代码
Nov 27 #Python
Python GUI库Tkiner使用方法代码示例
Nov 27 #Python
Python爬虫爬取有道实现翻译功能
Nov 27 #Python
python爬虫中采集中遇到的问题整理
Nov 27 #Python
You might like
模拟OICQ的实现思路和核心程序(一)
2006/10/09 PHP
php array_pop()数组函数将数组最后一个单元弹出(出栈)
2011/07/12 PHP
网页中实现浏览器的最大,最小化和关闭按钮
2007/03/12 Javascript
js实现的跟随鼠标移动的时钟效果(中英文日期显示)
2011/01/17 Javascript
jquery配合css简单实现返回顶部效果
2013/09/30 Javascript
Node.js 去掉种子(torrent)文件里的邪恶信息
2015/03/27 Javascript
以jQuery中$.Deferred对象为例讲解promise对象是如何处理异步问题
2015/11/13 Javascript
javascript伸缩型菜单实现代码
2015/11/16 Javascript
JavaScript匿名函数之模仿块级作用域
2015/12/12 Javascript
JavaScript实现搜索框的自动完成功能(一)
2016/02/25 Javascript
使用微信小程序开发前端【快速入门】
2016/12/05 Javascript
js多个物体运动功能实例分析
2016/12/20 Javascript
微信小程序 两种滑动方式(横向滑动,竖向滑动)详细及实例代码
2017/01/13 Javascript
使用JavaScriptCore实现OC和JS交互详解
2017/03/28 Javascript
jQuery+Ajax+js实现请求json格式数据并渲染到html页面操作示例
2020/06/02 jQuery
微信小程序实现底部弹出模态框
2020/11/18 Javascript
[52:07]完美世界DOTA2联赛PWL S3 LBZS vs access 第二场 12.10
2020/12/13 DOTA
python人人网登录应用实例
2014/09/26 Python
python获取图片颜色信息的方法
2015/03/18 Python
python 数据清洗之数据合并、转换、过滤、排序
2017/02/12 Python
教你用 Python 实现微信跳一跳(Mac+iOS版)
2018/01/04 Python
python2.7 json 转换日期的处理的示例
2018/03/07 Python
基于Python pip用国内镜像下载的方法
2018/06/12 Python
python版本单链表实现代码
2018/09/28 Python
在Qt5和PyQt5中设置支持高分辨率屏幕自适应的方法
2019/06/18 Python
python 正则表达式参数替换实例详解
2020/01/17 Python
Python3 webservice接口测试代码详解
2020/06/23 Python
李维斯法国官网:Levi’s法国
2019/07/13 全球购物
澳大利亚购买太阳镜和眼镜网站:Glamoureyes
2020/09/22 全球购物
大学生职业生涯规划书模板
2014/01/03 职场文书
代理班主任的自我评价
2014/02/04 职场文书
领导干部“四风”问题批评与自我批评材料
2014/09/24 职场文书
坎儿井导游词
2015/02/09 职场文书
个人党性分析总结
2015/03/05 职场文书
创业不要错过,这4种餐饮新模式
2019/07/18 职场文书
关于Python中*args和**kwargs的深入理解
2021/08/07 Python