在 Python 中锁定文件
在我们的日常生活中,有些资源是不能被两个人同时访问的,比如更衣室。 每个更衣室都有一把锁,一次只有一个人可以打开。
如果两个人同时访问,可能会造成混乱和尴尬。 所以,这就是共享资源受到锁保护的原因。
同样,在编程中,只要两个进程或线程共享同一资源,就会产生必须使用锁来避免的问题。 本文将解释如何在 Python 中锁定文件。
Python中多进程共享资源的影响
本节将阐明为什么在 Python 中锁定文件很重要。
该程序有两个过程:第一个向帐户中存入$1,另一个从中扣除$1,最后打印余额。 此操作在循环内运行数千次。
从技术上讲,迭代不会导致最终余额发生变化,因为一遍又一遍地添加和扣除相同的数字。 但是当程序被编译时,结果是可变的。
import multiprocessing
# Withdrawal function
def wthdrw(bal):
for _ in range(10000):
bal.value = bal.value - 1
# Deposit function
def dpst(bal):
for _ in range(10000):
bal.value = bal.value + 1
def transact():
# initial balance
bal = multiprocessing.Value('i', 100)
# creating processes
proc1 = multiprocessing.Process(target=wthdrw, args=(bal,))
proc2 = multiprocessing.Process(target=dpst, args=(bal,))
# starting processes
proc1.start()
proc2.start()
# waiting for processes to finish
proc1.join()
proc2.join()
# printing final balance
print("Final balance = {}".format(bal.value))
if __name__ == "__main__":
for _ in range(10):
# Performing transaction process
transact()
输出:
C:\python38\python.exe "C:\Users\Win 10\main.py"
Final balance = 428
Final balance = 617
Final balance = -1327
Final balance = 1585
Final balance = -130
Final balance = -338
Final balance = -73
Final balance = -569
Final balance = 256
Final balance = -426
Process finished with exit code 0
让我们分解示例代码以了解它发生的原因。
该程序使用 Python 库包 multiprocessing 将方法作为程序中的进程调用。
wthdrw 和 dpst 这两个方法,在账户中扣钱和加钱。 它们有一个参数,bal,代表平衡。
在方法内部,bal 的值在 for 循环内递增或递减。 迭代重复 10000 次。
bal.value 是这个程序中的共享资源。
# Method to Decrement
def wthdrw(bal):
for _ in range(10000):
bal.value = bal.value - 1
一种新的方法 transact()
可以一个接一个地启动进程。 在该方法内部,创建了存储初始值的对象 bal,并将余额设置为 100。
def transact():
bal = multiprocessing.Value('i', 100)
lock = multiprocessing.Lock()
multiprocessing 库允许使用对象将方法作为进程启动。 例如,方法 wthdrw 是通过创建进程 proc1 来启动的。
此过程以 wthdrw 方法为目标,并将 bal 和一个空对象作为参数传递。
proc1 = multiprocessing.Process(target=wthdrw, args=(bal,))
同样,创建一个 proc2 进程来启动 dpst 方法。 创建两个进程后,将使用 process_name.start() 启动它们。
proc1.start()
proc2.start()
要关闭正在运行的进程,使用 process_name.join() 语法。 它将请求排在正在运行的进程后面,因此系统会等待进程完成,然后将其关闭。
过程完成后打印最终余额。
print("Final balance = {}".format(bal.value))
__name__
变量设置为 __main
,并在 for 循环内调用方法 transact()
。 这让我们可以多次观察迭代,而无需手动调用流程。
if __name__ == "__main__":
for _ in range(10):
transact()
当程序运行时,我们发现数值不一致。 共享资源 bal.value 不受锁保护,因此其他进程在运行进程完成之前编辑该值。
锁就是用来解决这个问题的。 上面的程序通过在两种方法中放置一个锁来锁定; 这样,编译器必须等待在前一个进程完成之前运行下一个进程。
# Withdrawal function
def wthdrw(bal, lock):
for _ in range(10000):
# Locks
lock.acquire()
bal.value = bal.value - 1
lock.release()
lock.acquire()
语句锁定进程,并在其中编辑 bal 的值。 之后,使用 lock.release()
释放锁。
这同样适用于 dpst 方法,程序的其余部分保持不变。
输出:
C:\python38\python.exe "C:\Users\Win 10\main.py"
Final balance = 100
Final balance = 100
Final balance = 100
Final balance = 100
Final balance = 100
Final balance = 100
Final balance = 100
Final balance = 100
Final balance = 100
Final balance = 100
Process finished with exit code 0
正如我们知道共享资源的多个进程会发生什么,我们将研究在 Python 中锁定文件所需的重要功能 - 文件状态。
文件状态及其对 Python 中锁定文件的影响
文件状态显示文件是打开还是关闭。 了解文件状态以在 Python 中锁定文件很重要,因为锁定操作只能放在打开的文件上。
在 Python 中,假设有一个文件——“myFile.txt”。 当我们写:
readMyFile=open("myFile.txt","r").read()
无法获取文件的状态、关闭它或执行任何其他操作。 没有访问权限,因为它没有存储在任何地方。
由于丢弃信息没有任何价值,而且垃圾收集是不可能的,所以没有魔法可以让丢失的知识恢复。
避免存储任何打算在变量(列表、字典成员或实例的属性)中使用的值,这将允许使用它。
myFile = open("myFile.txt", "r")
readMyFile = myFile.read()
print(myFile.closed)
myFile.close()
print(myFile.closed)
上面的代码将文件保存在变量 myFile 中。 然后另一个变量 readMyFile 使用 myFile 变量读取对象。
即使在读取完成后,使用此方法也会使文件保持打开状态。
语法 print(myFile.closed) 显示当前文件状态; 在这里,它返回一个假值,这意味着它没有关闭。 使用 myFile.close() 显式关闭此文件。
由于显式关闭文件容易导致人为错误,更好的解决方案是使用 with 语句。
with open("myFile.txt", "r") as myFile:
readMyFile = myFile.read()
现在,myFile 会自动关闭而无需显式关闭它。 从上面的示例中,我们了解到打开和关闭文件的有效方法对于在 Python 中锁定文件至关重要。
文件只能在打开状态下被锁定,没有锁时无法解锁文件。 在 Python 中锁定文件必须始终在 with 块内执行。
让我们看看在 python 中锁定文件的几种方法。
在 Python 中锁定文件
文件锁通常是在脚本内部使用的第三方库来锁定文件。 在本节中,将讨论一些广泛使用的文件锁。
在 Python 中使用 FileLock 锁定文件
这是一个特定于平台的文件锁定库,可在 Windows 和 UNIX 上运行。 要安装它,请键入命令:
pip install filelock
使用此方法锁定文件非常简单。 在 with 语句中使用 FileLock 打开文件,因此文件打开,系统将其锁定。
处理完成后,在 with 语句结束时自动释放锁。
from filelock import FileLock
with FileLock("myfile.txt"):
print("Lock acquired.")
输出:
C:\python38\python.exe "C:/main.py"
Lock acquired.
Process finished with exit code 0
在 Python 中锁定文件的另一种方法是使用超时。 如果无法锁定文件,这有助于设置时间限制,从而释放其他进程的文件句柄。
from filelock import FileLock
file = "example.txt"
lockfile = "example.txt.lock"
lock = FileLock(lockfile, timeout=5)
lock.acquire()
with open(file, "a") as f:
print("File Locked")
f.write("Add some data \n")
lock.release()
with open(file, "a") as f:
f.write("Second time writing \n")
第一行代码从文件锁库中导入文件锁模块。 example.txt 文件保存在一个变量文件中; 该文件将用于在其上写入。
锁没有直接放在原始文件上,因此创建了一个临时文件,如 example.txt.lock。 变量lock用于为lockfile创建一个锁对象,设置超时时间为5秒。
可以使用 lock.acquire
语句放置此锁。 with 块用于打开文件并写入文件,而 lockfile 避免任何其他进程在写入时访问原始文件。
最后,使用 lock.release()
释放锁。 然后另一个进程打开文件并成功写入。
输出:
C:\python38\python.exe "C:/main.py"
File Locked
Process finished with exit code 0
要使用 Python 锁定文件,我们还可以以嵌套方式放置锁:
from filelock import Timeout, FileLock
lock = FileLock("high_ground.txt.lock")
with lock:
with open("high_ground.txt", "a") as f:
f.write("You were the chosen one.")
在上面的示例中,使用名为 high_ground.txt 的文件创建了一个锁定对象,以锁定 Python 中的一个文件。 锁放在一个块内; 在其中,使用另一个块读取文件。
这种方法比前一种方法需要更少的代码。
FileLock 是平台相关的。 如果每个应用程序实例都在同一平台上运行,请使用 FileLock; 否则,使用 SoftFileLock。
SoftFileLock 独立于平台,只监视锁定文件的存在。 正因为如此,它非常便携,并且在应用程序崩溃时更有可能死机。
在这种情况下,请删除锁定文件。
文件储物柜也可以与异常处理块一起使用:
try:
with lock.acquire(timeout=10):
with open(file_path, "a") as f:
f.write("I have a bad feeling about this.")
except Timeout:
print("Another instance of this application currently holds the lock.")
此代码片段在 try 块内放置了一个锁,并给出了 10 秒的超时。 然后在一个嵌套的里面,文件被写入。
在 except 块内,如果应用程序超时,将打印一条合适的消息。
在 Python 中使用 PortaLocker 锁定文件
在 Python 中锁定文件的另一种选择是使用名为 portalocker 的库,它提供了一个简单的文件锁定 API。
重要的是要记住,锁在 Linux 和 Unix 系统上默认是建议性的。 通过在 mount 命令中添加 -o mand 选项,可以在 Linux 上启用强制文件锁定。
要安装它,请键入命令:
pip install "portalocker[redis]"
使用 portalocker 在 Python 中锁定文件类似于 FileLock 方法,但更简单。
import portalocker
file = 'example.txt'
with portalocker.Lock(file, "a") as fh:
fh.write("first instance")
print('waiting for your input')
input()
输出:
C:\python38\python.exe "C:/main.py"
lock acquired
waiting for your input
在上面的代码中,第一行导入了库包。 使用语法 portalocker.RedisLock()
创建锁,并使用 with 语句放置锁。
要将锁置于超时内,请使用:
import portalocker
file = 'example.txt'
with portalocker.Lock('file', timeout=1) as fh:
print('writing some stuff to my cache...', file=fh)
输出:
总结
本文解释了如何在 Python 中锁定文件及其重要性。 阅读本文后,大家可以轻松创建开放状态文件并对其进行锁定。
相关文章
Django 中的 Slug
发布时间:2023/05/04 浏览次数:173 分类:Python
-
本篇文章旨在定义一个 slug 以及我们如何使用 slug 字段在 Python 中使用 Django 获得独特的帖子。
在 Django 中按降序过滤查询集中的项目
发布时间:2023/05/04 浏览次数:157 分类:Python
-
在这个讲解中,学习如何借助 Django 中的 order_by() 方法按降序过滤出查询集中的项目。
Django ALLOWED_HOSTS 介绍
发布时间:2023/05/04 浏览次数:181 分类:Python
-
本文展示了如何创建您的 Django 网站,为公开发布做好准备,如何设置 ALLOWED_HOSTS 以及如何在使用 Django 进行 Web 部署期间修复预期的主要问题。
Django 中的 Select_related 方法
发布时间:2023/05/04 浏览次数:129 分类:Python
-
本文介绍了什么是查询集,如何处理这些查询以及我们如何利用 select_related() 方法来过滤 Django 中相关模型的查询。
使用 Post 请求将数据发送到 Django 服务器
发布时间:2023/05/04 浏览次数:159 分类:Python
-
在这篇关于Django的讲解中,我们简要介绍了post和get请求以及如何在Django中用post实现CSRF token。
Django 返回 JSON
发布时间:2023/05/04 浏览次数:106 分类:Python
-
在与我们的讨论中,我们简要介绍了 JSON 格式,并讨论了如何借助 Django 中的 JsonResponse 类将数据返回为 JSON 格式。
在 Django 中创建对象
发布时间:2023/05/04 浏览次数:59 分类:Python
-
本文的目的是解释什么是模型以及如何使用 create() 方法创建对象,并了解如何在 Django 中使用 save() 方法。
在 Django 中为多项选择创建字段
发布时间:2023/05/04 浏览次数:75 分类:Python
-
在本文中,我们将着眼于为多项选择创建一个字段,并向您展示如何允许用户在 Django 中进行多项选择。