iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >使用python实现可重入的公平读写锁
  • 234
分享到

使用python实现可重入的公平读写锁

公平python可重入 2023-01-31 01:01:29 234人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

在本项目中,读写锁主要应用于多线程服务器场景下的日志文件的读写,以及缓存的获取和更新。 多线程编程的准标准库posix pthread库拥有rwlock, 而python2.7自带的threading库没有读写锁,只有可重入锁RL

在本项目中,读写主要应用于多线程服务器场景下的日志文件的读写,以及缓存的获取和更新。 多线程编程的准标准库posix pthread库拥有rwlock, 而python2.7自带的threading库没有读写锁,只有可重入锁RLock, 因此有必要自己实现一个读写锁以提升程序的并发性。

需要了解的概念

  1. 可重入锁。 可重入锁是指同一个锁可以多次被同一线程加锁而不会死锁。 实现可重入锁的目的是防止递归函数内的加锁行为,或者某些场景内无法获取锁A是否已经被加锁,这时如果不使用可重入锁就会对同一锁多次重复加锁,导致立即死锁。
  2. 读写锁。 读写锁与一般锁最大的区别是对同一共享资源多个线程的读取行为是并行的,同时保持该资源同一时刻只能由一个写进程独占,且写请求相对读请求有更高的优先级以防止writer starvation。( 一般锁同一时刻只能由一个线程独占,不论是读进程还是写进程, 即读写都是串行的,而读写锁读是并行的,写是串行的。)
    读写锁的特点是:
    2.1 当且仅当 锁没有被写进程占用且没有写请求时,可以获得读权限锁
    2.2 当且仅当 锁没有被占用且没有读写请求时,可以获得写权限锁
    读写锁的状态自动机可以参考下图
    rwlock_dfa
    所有数据库都拥有读写锁,当必要时,会自动将读锁提升为写锁,称为lock promotion。

使用读写锁的注意事项

  1. 慎用promote ! 读写锁一般都有提权函数promote()用于将一个已经获取读锁的线程进一步提权获得写锁,这样做很容易导致程序死锁。例如,两个均已经获取读锁的线程A和B同时调用promote函数尝试获得写权限,线程A发现存在读线程B,需要等待B完成以获取写锁,线程B发现存在读线程A,需要等待线程A完成以获取写锁,循环等待发生,程序死锁。因此,当且仅当你能确定当前仅有一个读线程占有锁时才能调用promote函数。一个已经获取读锁的线程提权最好的办法是先释放读锁,然后重新申请写锁。
  2. 使用多个锁时保证加解锁顺序相反。 考虑以下错误代码:
A.lock();
B.lock();
Foo();
A.unlock();
Bar();
B.unlock();

如果在Bar函数中尝试重新获取锁A,那么获取B锁之前先要获取A锁的语义就被破坏了,因为你尝试在拥有锁B的情况下获取锁A,而不是意图实现的相反情况,并且Bar函数在A锁的关键区之外,该实现有可能导致死锁或其它未定义的情况。
正确的实现应该是按照c++中的RaiI原则加解锁, 在Python中使用with语法

lockA=threading.lock()
lockB=threading.lock()
with lockA:
  with lockB:
    Foo();
  Bar()

读写锁目前的非官方实现

下列为目前发现的python rwlock的非官方实现
1. https://majid.info/blog/a-reader-writer-lock-for-python/
2. Https://hdknr.GitHub.io/docs/Django/modules/djanGo/utils/synch.html#RWLock
3. https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/
4. https://github.com/azraelxyz/rwlock/blob/master/rwlock/rwlock.py

存在的问题

由于4个实现全部贴出代码内容较长,因此这里略去。推荐阅读[1]和[4]的实现。
1. [1]. 使用条件变量实现, [2]. 使用信号量实现,实际效果没有区别(信号量类有内部计数器,既可以当锁又可以当条件变量),但在当前需求下使用条件变量的版本更通俗易懂且[2]. 没有测试代码。 [3]. 中测试代码最全且使用了unittest,但自己实现的信号量_LightSwitch的auquire和release语义和python threading库正好相反,不推荐。 [4]. 的实现最规范也最复杂,已经提交给了issue8800, 与其它3个实现的主要区别是自己实现了可重入锁, 但是没有promote和demote接口也没有测试代码。
2. 除了[2]和[4],其它两个个版本的锁都是不可重入的。
通过分析4个版本的源码可以看出,4个版本[1]的实现最均衡,唯一实现了promote和demote函数,代码也最清晰易懂,但是4个版本均存在无法完全解决writer starvation的问题(没有队列保证公平性,随机唤醒写线程,如果写线程较多可能会出现某一阻塞等待的写线程永远无法被唤醒的情况 )。

改进版的读写锁实现

针对[1]的改进主要包括两点:
1. 增加了写请求队列(python中threading.Queue是线程安全的), 唤醒写线程时按照FIFO实现公平调度,避免大量写进程等待时可能发生的writer starvation
2. 将threading.lock改为可重入的threading.Rlock
3. 如果对同时并发读取的线程数有限制,则可以在RWLock的构造函数__init__中定义一个最大同时读取数max_reader_num,同时将acquire_read中的条件判断替换为:

while self.rwlock < 0 or self.rwlock == max_reader_num or self.writers_waiting:

即可实现限制并发读取的最大线程数。

改进的实现代码

import threading

from Queue import Queue

class RWLock:
        """
        A simple reader-writer lock Several readers can hold the lock
        simultaneously, XOR one writer. Write locks have priority over reads to
        prevent write starvation. wake up writer accords to FIFO
        """
          def __init__(self):
            self.wait_writers_q=Queue()
            self.rwlock = 0
            self.writers_waiting = 0
            self.monitor = threading.RLock()
            self.readers_ok = threading.Condition(self.monitor)

          def acquire_read(self):
            """Acquire a read lock. Several threads can hold this typeof lock.
        It is exclusive with write locks."""
            self.monitor.acquire()
            while self.rwlock < 0 or self.writers_waiting:
              self.readers_ok.wait()
            self.rwlock += 1
            self.monitor.release()

          def acquire_write(self):
            """Acquire a write lock. Only one thread can hold this lock, and
        only when no read locks are also held."""
            self.monitor.acquire()
            while self.rwlock != 0:
              self.writers_waiting += 1
              writers_ok= threading.Condition(self.monitor)
              self.wait_writers_q.put(writers_ok)
              writers_ok.wait()
              self.writers_waiting -= 1
            self.rwlock = -1
            self.monitor.release()

          def promote(self):
            """Promote an already-acquired read lock to a write lock
            WARNING: it is very easy to deadlock with this method"""
            self.monitor.acquire()
            self.rwlock -= 1
            while self.rwlock != 0:
              self.writers_waiting += 1
              writers_ok= threading.Condition(self.monitor)
              self.wait_writers_q.put(writers_ok)
              writers_ok.wait()
              self.writers_waiting -= 1
            self.rwlock = -1
            self.monitor.release()

          def demote(self):
            """Demote an already-acquired write lock to a read lock"""
            self.monitor.acquire()
            self.rwlock = 1
            self.readers_ok.notifyAll()
            self.monitor.release()

          def release(self):
            """Release a lock, whether read or write."""
            self.monitor.acquire()
            if self.rwlock < 0:
              self.rwlock = 0
            else:
              self.rwlock -= 1
            wake_writers = self.writers_waiting and self.rwlock == 0
            wake_readers = self.writers_waiting == 0
            self.monitor.release()
            if wake_writers:
              # print "wake write..."
              writers_ok=self.wait_writers_q.get_nowait()
              writers_ok.acquire()
              writers_ok.notify()
              writers_ok.release()
            elif wake_readers:
              self.readers_ok.acquire()
              self.readers_ok.notifyAll()
              self.readers_ok.release()

测试代码

if __name__ == '__main__':
        import time
        rwl = RWLock()
        class Reader(threading.Thread):
          def run(self):
            print self, 'start'
            rwl.acquire_read()
            print self, 'acquired'
            time.sleep(5)
            print self, 'stop'
            rwl.release()

        class Writer(threading.Thread):
          def run(self):
            print self, 'start'
            rwl.acquire_write()
            print self, 'acquired'
            time.sleep(10)
            print self, 'stop'
            rwl.release()
            rwl.release()

        class ReaderWriter(threading.Thread):
          def run(self):
            print self, 'start'
            rwl.acquire_read()
            print self, 'acquired'
            time.sleep(5)
            rwl.promote()
            print self, 'promoted'
            time.sleep(5)
            print self, 'stop'
            rwl.release()

        class WriterReader(threading.Thread):
          def run(self):
            print self, 'start'
            rwl.acquire_write()
            print self, 'acquired'
            time.sleep(10)
            print self, 'demoted'
            rwl.demote()
            time.sleep(10)
            print self, 'stop'
            rwl.release()

        Reader().start()
        time.sleep(1)
        Reader().start()
        time.sleep(1)
        ReaderWriter().start()
        time.sleep(1)
        WriterReader().start()
        time.sleep(1)
        Reader().start()
        for i in range(5):
                time.sleep(1)
                Writer().start()

测试输出:

[rstudio2@Rserver1 MgtvData]$ python rwlock_test.py
<Reader(Thread-1, started 140256753055488)> start
<Reader(Thread-1, started 140256753055488)> acquired
<Reader(Thread-2, started 140256742565632)> start
<Reader(Thread-2, started 140256742565632)> acquired
<ReaderWriter(Thread-3, started 140256732075776)> start
<ReaderWriter(Thread-3, started 140256732075776)> acquired
<WriterReader(Thread-4, started 140256519124736)> start
<Reader(Thread-5, started 140256508634880)> start
<Reader(Thread-1, started 140256753055488)> stop
<Writer(Thread-6, started 140256753055488)> start
<Reader(Thread-2, started 140256742565632)> stop
<Writer(Thread-7, started 140256742565632)> start
<ReaderWriter(Thread-3, started 140256732075776)> promoted
<Writer(Thread-8, started 140256498145024)> start
<Writer(Thread-9, started 140256487655168)> start
<Writer(Thread-10, started 140256477165312)> start
<ReaderWriter(Thread-3, started 140256732075776)> stop
<WriterReader(Thread-4, started 140256519124736)> acquired
<WriterReader(Thread-4, started 140256519124736)> demoted
<WriterReader(Thread-4, started 140256519124736)> stop
<Writer(Thread-6, started 140256753055488)> acquired
<Writer(Thread-6, started 140256753055488)> stop
<Writer(Thread-7, started 140256742565632)> acquired
<Writer(Thread-7, started 140256742565632)> stop
<Writer(Thread-8, started 140256498145024)> acquired
<Writer(Thread-8, started 140256498145024)> stop
<Writer(Thread-9, started 140256487655168)> acquired
<Writer(Thread-9, started 140256487655168)> stop
<Writer(Thread-10, started 140256477165312)> acquired
<Writer(Thread-10, started 140256477165312)> stop
<Reader(Thread-5, started 140256508634880)> acquired
<Reader(Thread-5, started 140256508634880)> stop

可以看到, 从thread6开始的写进程被依次按照请求的顺序唤醒。

扩展阅读

  1. spin-lock使用while循环的目的是解决spurious wakeup
  2. 使用信号量的目的是解决missed signal
  1. http://tutorials.jenkov.com/java-concurrency/read-write-locks.html

--结束END--

本文标题: 使用python实现可重入的公平读写锁

本文链接: https://www.lsjlt.com/news/184128.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 使用python实现可重入的公平读写锁
    在本项目中,读写锁主要应用于多线程服务器场景下的日志文件的读写,以及缓存的获取和更新。 多线程编程的准标准库posix pthread库拥有rwlock, 而python2.7自带的threading库没有读写锁,只有可重入锁RL...
    99+
    2023-01-31
    公平 python 可重入
  • Java 重入锁和读写锁的具体使用
    目录重入锁 1. 实现重进入 2. 公平与非公平获取锁的区别 读写锁 1. 接口示例 2. 读写状态的设计 3. 写锁的获取与释放 4. 读锁的获取与释放 5. 锁降级 重入锁 重...
    99+
    2024-04-02
  • Java 重入锁和读写锁怎么使用
    这篇文章主要介绍“Java 重入锁和读写锁怎么使用”,在日常操作中,相信很多人在Java 重入锁和读写锁怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java 重入锁和读写锁怎么使用”的疑惑有所帮助!...
    99+
    2023-06-08
  • Curator实现分布式锁(可重入 不可重入 读写 联锁 信号量 栅栏 计数器)
    文章目录 前言代码实践1. 配置2. 可重入锁InterProcessMutex3. 不可重入锁InterProcessSemaphoreMutex4. 可重入读写锁InterProcessReadWriteLock5. 联锁Int...
    99+
    2023-08-19
    分布式 java 服务器
  • Redis如何实现可重入锁的设计
    这篇文章主要介绍Redis如何实现可重入锁的设计,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!但是仍然有些场景是不满⾜的,例如⼀ 个⽅法获取到锁之后,可能在⽅法内调这个⽅法此时就获取...
    99+
    2024-04-02
  • Golang实现可重入锁的示例代码
    目录什么是可重入锁具体实现项目中遇到了可重入锁的需求和实现,具体记录下。 什么是可重入锁 我们平时说的分布式锁,一般指的是在不同服务器上的多个线程中,只有一个线程能抢到一个锁,从而执...
    99+
    2024-04-02
  • redis分布式锁之可重入锁的实现代码
    上篇redis实现的分布式锁,有一个问题,它不可重入。 所谓不可重入锁,即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞。 同一个人拿一个锁 ...
    99+
    2024-04-02
  • java中怎么实现可重入的自旋锁
    这篇文章主要介绍了java中怎么实现可重入的自旋锁的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇java中怎么实现可重入的自旋锁文章都会有所收获,下面我们一起来看看吧。说明是指试图获得锁的线程不会堵塞,而是通过...
    99+
    2023-06-30
  • java可重入锁的使用场景有哪些
    可重入锁是指同一个线程可以多次获得同一把锁,在释放锁之前需要释放相同次数的锁。可重入锁的使用场景包括:1. 递归函数:当一个递归函数...
    99+
    2023-09-11
    java
  • Python利用pdfplumber实现读取PDF写入Excel
    目录一、Python操作PDF 13大库对比二、pdfplumber模块1.安装2. 加载PDF3. pdfplumber.PDF类4. pdfplumber.Page类三、实战操作...
    99+
    2024-04-02
  • 怎么在java中实现内置锁的可重入性
    这篇文章给大家介绍怎么在java中实现内置锁的可重入性,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。java基本数据类型有哪些Java的基本数据类型分为:1、整数类型,用来表示整数的数据类型。2、浮点类型,用来表示小数...
    99+
    2023-06-14
  • 详解Java ReentrantLock可重入,可打断,锁超时的实现原理
    目录概述可重入可打断锁超时概述 前面讲解了ReentrantLock加锁和解锁的原理实现,但是没有阐述它的可重入、可打断以及超时获取锁失败的原理,本文就重点讲解这三种情况。建议大家先...
    99+
    2022-11-13
    Java ReentrantLock可重入 Java ReentrantLock可打断 Java ReentrantLock锁超时 Java ReentrantLock
  • Java利用StampedLock实现读写锁的方法详解
    目录概述StampedLock介绍演示例子性能对比总结概述 想到读写锁,大家第一时间想到的可能是ReentrantReadWriteLock。实际上,在jdk8以后,java提供了一...
    99+
    2022-11-13
    Java StampedLock读写锁 Java StampedLock Java 读写锁
  • 怎么在java中实现一个可重入的自旋锁
    怎么在java中实现一个可重入的自旋锁?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Java可以用来干什么Java主要应用于:1. web开发;2. Android开发;3. ...
    99+
    2023-06-14
  • 利用python实现ftp的文件读写
    ftp登陆连接 from ftplib import FTP #加载ftp模块 ftp=FTP() #设置变量 ftp.set_debuglevel(2) ...
    99+
    2023-01-31
    文件 python ftp
  • python中csv文件的写入与读取怎么实现
    这篇文章主要讲解了“python中csv文件的写入与读取怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“python中csv文件的写入与读取怎么实现”吧!CSV (Comma Sepa...
    99+
    2023-06-29
  • 使用python怎么实现一个文件读写函数
    本篇文章给大家分享的是有关使用python怎么实现一个文件读写函数,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。用到xlrd库函数需要预先install命令:pip insta...
    99+
    2023-06-06
  • 如何使用h5+js实现本地文件读取和写入
    小编给大家分享一下如何使用h5+js实现本地文件读取和写入,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!   代码如下:   读取本地文件   <!doctypehtml>...
    99+
    2024-04-02
  • 利用Python实现读取Word表格计算汇总并写入Excel
    目录前言一、首先导入包二、读评价表所在的目录文件三、读word文件,处理word中的表格数据四、统计计算五、将统计计算结果写入汇总Excel完整代码总结前言 快过年了,又到了公司年底...
    99+
    2024-04-02
  • Python使用os模块实现更高效地读写文件
    目录使用 os.open 打开文件使用 os.read 读取文件使用 os.write 写入文件使用 os.open 打开文件 无论是读文件还是写文件,都要先打开...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作