首页 > 编程语言 >Python文件锁portalocker模块

Python文件锁portalocker模块

时间:2023-11-24 18:33:43浏览次数:33  
标签:文件 Python portalocker 进程 模块 test import pickle

  在多进程/多线程的学习后,终于来到了“文件锁”这个概念阶段,文件锁的存在就是由于在多进程/线程操作时会对某个文件进行频繁修改,而导致读取与修改的数据产生不同步。典型场景有以下:

  • 进程1对文件A进行写入操作,写入一条记录a,持续时间时20s才能完成这个文件的写入。此时进程2在第5s时也开始对文件A进行修改,写入一条记录b,但只用了2s就写入了。这种情况下进程1完成写入记录a后,其实并不知道记录b已经在它之前就插入了,所以在进程1和进程2的”视角“中,这一份文件A的数据是不一样的。
  • 进程1对文件A进行写入操作,写入一条记录a,持续时间时20s才能完成这个文件的写入。此时进程2在第5s时,读取文件A,但由于记录a还没有被插入完成,所以在进程2的”视角“里,文件A中并没有记录a。

  以上两种典型情况在大并发量时都会导致不同进程中对同一份文件拿到的时不同的数据,此时我们就需要锁进行解决。由于python中对于unix系统存在fcntl标准模块,对于window具有

msvcrt标准模块,但这两个模块都只能在分别的操作系统中使用,而第三方包portalocker模块则对这两个系统做了兼容(这也是本文的主角)。

锁的基本理解

  在讲解包的使用之前,我们要明确锁的工作原理(无论是文件锁还是对象锁)。锁相当于授权,可以理解为只有当某个进程拿到了这个授权,才能执行之后的操作。而没有拿到授权就进行操作(对文件操作/ 对象操作)就会报错。因此在操作前我们需要拿到锁!对于锁我们还需要明确的时,加锁的进程,需要主动释放锁。在锁没有释放的情况下,其他进程时无法去获取到锁的,其他进程只能阻塞当前进程等待(或者抛出错误,当然我们也可以设定一个等待时间,超时了我就不等了)。

portalocker包的使用

  这个包的使用方式与常见的锁的使用方式都是一样的,最重要的就是加锁和释放锁。

  • portalocker.lock(文件权柄, portalocker.LOCK_SH):给文件赋予(尝试获得)共享锁,若不能获得则,阻塞当前进程,一般在只读文件时使用
  • portalocker.lock(文件权柄, portalocker.LOCK_EX):给文件赋予(尝试获得)独占锁,若不能获得则,阻塞当前进程, 一般在修改文件时使用
  • portalocker.lock(文件权柄, portalocker.LOCK_EX | portalocker.LOCK_NB) :  给文件赋予(尝试获得)独占锁,若不能获得则,则直接抛出异常

  我们需要注意的是以上三个方法都具有两个功能,第一个功能是给文件加锁,第二个功能是尝试获得这个文件的锁。我们直接从例子来看。由于我们需要模拟多进程情况,因此我们有三个.py文件,并且分别在不同终端中执行。

文件1:test.py

import pickle
import portalocker
import time


with open('test.pkl', mode="rb+") as f:
    portalocker.lock(f, portalocker.LOCK_EX) # 加锁 / 获取锁独占
    print("已增加锁")
## 以下代码在字典上新增元素 test = pickle.load(f) ## # 这里可以多test进行修改 ##
time.sleep(10) # 模拟阻塞 pickle.dump(test, f) portalocker.unlock(f) # 释放锁 print(f"已释放锁{test}")

  文件2:test2.py

import pickle
import time
import portalocker


with open('test.pkl', 'rb') as f:
    portalocker.lock(f, portalocker.LOCK_SH)  # 获取/加锁 共享锁
    test = pickle.load(f)
    time.sleep(5)  # 模拟阻塞
    print(test)
    portalocker.unlock(f)

  文件3:test3.py

import pickle
import portalocker

with open('test.pkl', 'rb') as f:
    portalocker.lock(f, portalocker.LOCK_SH)
    test = pickle.load(f)
    print(test)
    portalocker.unlock(f)

  分别解释下上述三个文件:文件1模拟对一个.pkl文件读写操作,并修改其结构,最后再将其保存,使用的是独占锁。文件2模拟对用一个.pkl文件进行只读,使的是共享锁。文件3是也是模拟对同一个.pkl文件进行只读,使的是也是共享锁。开始实验:

  实验一:短间隔(1s)先执行文件1,再执行文件2

   左边在执行时,右边开始了阻塞。直到当左边的独占锁释放了,右边才会获得锁,从而执行后续流程。我们可以知道当我某一个进程拿到了独占锁时,其余进程的共享锁、独占锁都会被阻塞等待这个独占锁的释放。这就解决了当我们在更改某个文件时,其他进程不会拿到老版本的文件数据。只有当文件锁释放后,其他进程才拿到锁,进而执行后续的操作。注意这个我们在文件2中是在读取文件前(第八行)去请求锁(第七行),而不是早操作后,因为如果不请求锁而直接操作,就会直接抛出portalocker.LockException异常。

  实验二:短间隔(1s)先执行文件2,再执行文件1

   上图是在执行中的截图,我们可以看到,当文件2执行完成后,文件1才开始执行。说明当一个进程获得了某个文件的共享锁,其他进程是无法获得该文件的独占锁的,需要等到共享锁释放后,才能对这个文件拿到独占锁。

  实验三:短间隔(1s)先执行文件2,再执行文件3

   上图在文件2执行后,文件3仍可以正常执行,说明当一个进程给某个文件加上了共享锁,其余进程仍可以拿到共享锁,并执行自己后续的代码。这就解决了在读取文件时我们不用等待其他读取进程释放锁,这样就极大的提升了效率。

超时时间

  前面提到了,我们可以设置等待锁的超时时间,我们在使用以下方式时可以使用timeout功能来防止死锁。

import pickle
import portalocker
import time

with portalocker.Lock('test.pkl', mode="rb+", timeout=100) as f:
    print("已增加锁")
    test = pickle.load(f)
    time.sleep(10)
    pickle.dump(test, f)

    print(f"已释放锁{test}")

  上面代码是在文件1的基础上改进的(其功能完全相同),我们不使用open来打开文件,而使用 portalocker.Lock 来打开文件。这时我们可以不用显式的进行上锁和解锁,并且我们还可以使用timeout来设定最长等待获取锁的时间(如果在这个时间后还没有获得锁,那么就抛出异常,从而防止了死锁的存在)。

  不过我们需要注意的是,前面三个portalocker.lock方法都不能设定timeout。并且portalocker.Lock()默认使用的只有独占锁,并且无法设定为共享锁。因此如果你有对共享锁也想使用timeout的方式来实现防止死锁,就只能自己写计时了。

 

标签:文件,Python,portalocker,进程,模块,test,import,pickle
From: https://www.cnblogs.com/CircleWang/p/17854078.html

相关文章

  • 使用python包os.system执行命令与直接在终端执行命令的区别
     区别:1.使用systemctl命令执行时,即使是以root用户身份执行,也会关注服务的用户组权限。这是因为systemctl在执行服务管理操作时,会检查服务的配置文件中设置的用户和用户组权限,并确保当前用户具有足够的权限来管理服务。2.当你以root用户身份使用os.system执行命令时,它可能会绕......
  • python文字转语音
     pipinstallpygamepipinstallgtts importosimportpygamefromgttsimportgTTStext="你有新的美团外卖订单,请注意查收!"tts=gTTS(text,lang='zh',slow=False,)tts.save("./order.mp3")#os.system("start./order.mp3&quo......
  • python 类直接调用属性和方法
    classMyClass:class_variable="Thisisaclassvariable"@staticmethoddefstatic_method():print("Thisisastaticmethod")@classmethoddefclass_method(cls):print("Thisisaclassmethod......
  • 【Django基础】auth认证模块
    https://www.cnblogs.com/DuoDuosg/p/17005583.html一、django的auth认证模块1.什么是auth模块Auth模块是Django自带的用户认证模块:我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,......
  • (Python)基于对称点模式(Symmetrized Dot Pattern,SDP)的多元、多通道、多传感器信号融合
    对称点模式(SymmetrizedDotPattern,SDP)算法可将复杂时间序列以散点的形式清晰映射在极坐标图中,可以使原始时域信号通过图形化的方式提高可视化能力。因为极坐标图像的特殊性,多元、多通道、多传感器信号信息可通过SDP方法融合在有限区域中。适用于多元、多通道、多传感器信号的融合......
  • python wps异常处理: 服务启动异常-发生意外
    服务异常:找到单元xlwings._xlwindowsself._xl=COMRetryObjectWrapper(DispatchEx("ket.Application"))#内核不支持,只有was无excel参考:https://blog.csdn.net/oldman_g/article/details/124022289 ---发生意外:路径不完整,保存完整路径 ......
  • 一文彻底看懂Python切片,Python切片理解与操作
    1.什么是切片切片是Python中一种用于操作序列类型(如列表、字符串和元组)的方法。它通过指定起始索引和结束索引来截取出序列的一部分,形成一个新的序列。切片是访问特定范围内的元素,就是一个Area。说个笑话:切片不是切片,而是切片,但是又是切片。大家理解下呢(末尾放出作者自己的理解......
  • python glob
    glob是python自带的一个操作文件的相关模块,用它可以查找符合特定规则的文件路径名。使用该模块查找文件,只需要用到:“*”,“?”,“[]”这三个匹配符;”*”匹配0个或多个字符;”?”匹配单个字符;”[]”匹配指定范围内的字符,如:[0-9]匹配数字。glob.glob:返回所有匹配的文件路径列表......
  • python+playwright 学习-43 Pyinstaller打包生成独立的可执行文件
    前言playwright与Pyinstaller结合使用来创建独立的可执行文件。本地化安装有同学提到说想打成一个exe的独立包,但是执行playwrightinstall会默认把chromium,firefox和webkit三个浏览器安装到系统目录。这样打包的时候就找不到启动的浏览器文件。于是就想到把浏览器文件下......
  • python可视化打包exe
    安装Auto-py-to-exePython环境要大于或等于2.7然后在cmd里面输入pipinstallauto-py-to-exe安装完成之后,我们就可以在cmd输入一下命令启动auto-py-to-exe启动之后可以切换语言为中文,剩下的就看情况操作了......