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绘制人人网好友关系图示例
Apr 01 Python
Python中实现字符串类型与字典类型相互转换的方法
Aug 18 Python
Python中有趣在__call__函数
Jun 21 Python
Python的pycurl包用法简介
Nov 13 Python
使用paramiko远程执行命令、下发文件的实例
Oct 01 Python
python实现三维拟合的方法
Dec 29 Python
python单线程文件传输的实例(C/S)
Feb 13 Python
python多线程调用exit无法退出的解决方法
Feb 18 Python
详解Python odoo中嵌入html简单的分页功能
May 29 Python
Python 中Django安装和使用教程详解
Jul 03 Python
numpy数组广播的机制
Jul 12 Python
手动安装python3.6的操作过程详解
Jan 13 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
JoshChen_web格式编码UTF8-无BOM的小细节分析
2013/08/16 PHP
Apache服务器下防止图片盗链的办法
2015/07/06 PHP
php生成验证码函数
2015/10/20 PHP
Laravel 对某一列进行筛选然后求和sum()的例子
2019/10/10 PHP
web 页面分页打印的实现
2009/06/22 Javascript
Javascript 获取滚动条位置等信息的函数
2009/09/08 Javascript
让新消息在网页标题闪烁提示的jQuery代码
2013/11/04 Javascript
移动端横屏的JS代码(beta)
2016/05/16 Javascript
jQuery Validate 相关参数及常用的自定义验证规则
2017/03/06 Javascript
在Vue组件上动态添加和删除属性方法
2018/02/23 Javascript
基于angular6.0实现的一个组件懒加载功能示例
2018/04/12 Javascript
使用javascript做在线算法编程
2018/05/25 Javascript
Vue自定义指令封装节流函数的方法示例
2018/07/09 Javascript
微信小程序实现的点击按钮 弹出底部上拉菜单功能示例
2018/12/20 Javascript
javascript设计模式之装饰者模式
2020/01/30 Javascript
微信小程序复选框实现多选一功能过程解析
2020/02/14 Javascript
处理JavaScript值为undefined的7个小技巧
2020/07/28 Javascript
vue-router 2.0 跳转之router.push()用法说明
2020/08/12 Javascript
python实现Dijkstra静态寻路算法
2019/01/17 Python
python3.7 利用函数os pandas利用excel对文件名进行归类
2019/09/29 Python
Django 路由层URLconf的实现
2019/12/30 Python
jupyter修改文件名方式(TensorFlow)
2020/04/21 Python
Python基于xlrd模块处理合并单元格
2020/07/28 Python
python实现简单贪吃蛇游戏
2020/09/29 Python
Nisbets爱尔兰:英国最大的厨房和餐饮设备供应商
2019/01/26 全球购物
什么是GWT的Entry Point
2013/08/16 面试题
实习自我评价怎么写
2013/12/02 职场文书
初中体育教学反思
2014/01/14 职场文书
社区优秀志愿者材料
2014/02/02 职场文书
设计师个人求职信范文
2014/02/02 职场文书
优秀学生干部个人事迹材料
2014/06/02 职场文书
大学生考试作弊检讨书
2014/09/21 职场文书
2015年国庆节活动总结
2015/03/23 职场文书
公司聚餐通知
2015/04/22 职场文书
Redis字典实现、Hash键冲突及渐进式rehash详解
2021/09/04 Redis
Redis数据同步之redis shake的实现方法
2022/04/21 Redis