用Python实现读写锁的示例代码


Posted in Python onNovember 05, 2018

起步

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中获取网页状态码的两个方法
Nov 03 Python
Python与shell的3种交互方式介绍
Apr 11 Python
python 转换 Javascript %u 字符串为python unicode的代码
Sep 06 Python
Python实现脚本锁功能(同时只能执行一个脚本)
May 10 Python
Python设计模式之状态模式原理与用法详解
Jan 15 Python
Python叠加两幅栅格图像的实现方法
Jul 05 Python
Python替换月份为英文缩写的实现方法
Jul 15 Python
Python字典推导式将cookie字符串转化为字典解析
Aug 10 Python
Python实现平行坐标图的绘制(plotly)方式
Nov 22 Python
tensorflow 实现自定义梯度反向传播代码
Feb 10 Python
tensorflow 获取checkpoint中的变量列表实例
Feb 11 Python
Python中常见的数制转换有哪些
May 27 Python
详解如何为eclipse安装合适版本的python插件pydev
Nov 04 #Python
详解Python下Flask-ApScheduler快速指南
Nov 04 #Python
Python中修改字符串的四种方法
Nov 02 #Python
Python中flatten( )函数及函数用法详解
Nov 02 #Python
[原创]Python入门教程5. 字典基本操作【定义、运算、常用函数】
Nov 01 #Python
Python拼接字符串的7种方法总结
Nov 01 #Python
在python中bool函数的取值方法
Nov 01 #Python
You might like
PHP中用hash实现的数组
2011/07/17 PHP
php使用正则过滤js脚本代码实例
2014/05/10 PHP
php二维码生成
2015/10/19 PHP
PHP实现的激活用户注册验证邮箱功能示例
2017/06/06 PHP
PHP实现打包下载文件的方法示例
2017/10/07 PHP
统一接口:为FireFox添加IE的方法和属性的js代码
2007/03/25 Javascript
js相册效果代码(点击创建即可)
2013/04/16 Javascript
js 窗口抖动示例
2013/09/04 Javascript
jquery中的$(document).ready()使用小结
2014/02/14 Javascript
js实现页面跳转重定向的几种方式
2014/05/29 Javascript
Node.js实现简单聊天服务器
2014/06/20 Javascript
JavaScript中输出标签的方法
2014/08/27 Javascript
JavaScript中的object转换函数toString()与valueOf()介绍
2014/12/31 Javascript
jQuery插件bxSlider实现响应式焦点图
2015/04/12 Javascript
实例详解jQuery表单验证插件validate
2016/01/18 Javascript
微信小程序 动态绑定事件并实现事件修改样式
2017/04/13 Javascript
Angularjs根据json文件动态生成路由状态的实现方法
2017/04/17 Javascript
详细讲解如何创建, 发布自己的 Vue UI 组件库
2019/05/29 Javascript
python snownlp情感分析简易demo(分享)
2017/06/04 Python
详解Python 模拟实现生产者消费者模式的实例
2017/08/10 Python
Python调用C语言的方法【基于ctypes模块】
2018/01/22 Python
Python爬虫实战:分析《战狼2》豆瓣影评
2018/03/26 Python
详解Python3 中hasattr()、getattr()、setattr()、delattr()函数及示例代码数
2018/04/18 Python
Python多线程处理实例详解【单进程/多进程】
2019/01/30 Python
tensorflow ckpt模型和pb模型获取节点名称,及ckpt转pb模型实例
2020/01/21 Python
python GUI库图形界面开发之PyQt5窗口控件QWidget详细使用方法
2020/02/26 Python
Python闭包及装饰器运行原理解析
2020/06/17 Python
html5 canvas 实现光线沿不规则路径运动
2020/04/20 HTML / CSS
非凡女性奢华谦虚风格:The Modist
2017/10/28 全球购物
英国信箱在线鲜花速递公司:Bloom & Wild
2019/03/10 全球购物
德国珠宝和配件商店:Styleserver
2021/02/23 全球购物
在网络中有两台主机A和B,并通过路由器和其他交换设备连接起来,已经确认物理连接正确无误,怎么来测试这两台机器是否连通?如果不通,怎么来判断故障点?怎么排
2014/01/13 面试题
甜点店创业计划书
2014/01/27 职场文书
病假条格式范文
2015/08/17 职场文书
2019西餐厅创业计划书范文!
2019/07/12 职场文书
SQL Server实现分页方法介绍
2022/03/16 SQL Server