在 Python 中锁定文件

2024-12-09 08:30:00
admin
原创
138
摘要:问题描述:我需要锁定一个文件以使用 Python 进行写入。它将同时被多个 Python 进程访问。我在网上找到了一些解决方案,但大多数都无法满足我的目的,因为它们通常仅基于 Unix 或 Windows。解决方案 1:截至 2024 年 6 月的更新如今,似乎有许多强大的、跨平台的、积极维护的解决方案。其他...

问题描述:

我需要锁定一个文件以使用 Python 进行写入。它将同时被多个 Python 进程访问。我在网上找到了一些解决方案,但大多数都无法满足我的目的,因为它们通常仅基于 Unix 或 Windows。


解决方案 1:

截至 2024 年 6 月的更新

如今,似乎有许多强大的、跨平台的、积极维护的解决方案。其他答案和评论中引用最多的一些是:

  • 文件锁

  • 门户锁

  • oslo.concurrency(如果您需要更多通用的多进程同步实用程序)

原始答案

好吧,我最终还是选择了我在这里写的代码,我的网站上的链接已经失效了,可以在archive.org上查看(也可以在GitHub上查看)。我可以按以下方式使用它:

from filelock import FileLock

with FileLock("myfile.txt.lock"):
    # work with the file as it is now locked
    print("Lock acquired.")

解决方案 2:

其他解决方案引用了大量外部代码库。如果您更愿意自己动手​​,这里有一些跨平台解决方案的代码,该解决方案使用 Linux / DOS 系统上的相应文件锁定工具。

try:
    # Posix based file locking (Linux, Ubuntu, MacOS, etc.)
    #   Only allows locking on writable files, might cause
    #   strange results for reading.
    import fcntl, os
    def lock_file(f):
        if f.writable(): fcntl.lockf(f, fcntl.LOCK_EX)
    def unlock_file(f):
        if f.writable(): fcntl.lockf(f, fcntl.LOCK_UN)
except ModuleNotFoundError:
    # Windows file locking
    import msvcrt, os
    def file_size(f):
        return os.path.getsize( os.path.realpath(f.name) )
    def lock_file(f):
        msvcrt.locking(f.fileno(), msvcrt.LK_RLCK, file_size(f))
    def unlock_file(f):
        msvcrt.locking(f.fileno(), msvcrt.LK_UNLCK, file_size(f))


# Class for ensuring that all file operations are atomic, treat
# initialization like a standard call to 'open' that happens to be atomic.
# This file opener *must* be used in a "with" block.
class AtomicOpen:
    # Open the file with arguments provided by user. Then acquire
    # a lock on that file object (WARNING: Advisory locking).
    def __init__(self, path, *args, **kwargs):
        # Open the file and acquire a lock on the file before operating
        self.file = open(path,*args, **kwargs)
        # Lock the opened file
        lock_file(self.file)

    # Return the opened file object (knowing a lock has been obtained).
    def __enter__(self, *args, **kwargs): return self.file

    # Unlock the file and close the file object.
    def __exit__(self, exc_type=None, exc_value=None, traceback=None):        
        # Flush to make sure all buffered contents are written to file.
        self.file.flush()
        os.fsync(self.file.fileno())
        # Release the lock on the file.
        unlock_file(self.file)
        self.file.close()
        # Handle exceptions that may have come up during execution, by
        # default any exceptions are raised to the user.
        if (exc_type != None): return False
        else:                  return True        

现在,可以在通常使用语句的块AtomicOpen中使用。with`open`

警告:

  • 如果在 Windows 上运行时,Python 在调用退出之前崩溃,我不确定锁定行为会是什么样子。

  • 此处提供的锁定是建议性的,而不是绝对的。所有可能竞争的进程都必须使用“AtomicOpen”类。

  • 截至 (2020 年 11 月 9 日),此代码仅锁定Posix 系统上的可写文件。在发布之后和此日期之前的某个时间点,fcntl.lock在只读文件上使用 已变得非法。

解决方案 3:

这里有一个跨平台文件锁定模块:Portalocker

尽管正如凯文所说,如果可能的话,你会想避免同时从多个进程写入一个文件。

如果你能将问题强行塞进数据库,那么你可以使用 SQLite。它支持并发访问并处理自己的锁定。

解决方案 4:

我一直在寻找几种解决方案来实现这一点,我的选择是
oslo.concurrency

它功能强大,文档齐全。它基于紧固件。

其他解决方案:

  • Portalocker:需要 pywin32,这是一个 exe 安装,因此无法通过 pip 安装

  • 紧固件:记录不全

  • lockfile:已弃用

  • flufl.lock:POSIX 系统的 NFS 安全文件锁定。

  • simpleflock:最后更新 2013-07

  • zc.lockfile:最后更新 2016-06(截至 2017-03)

  • lock_file:最后更新于 2007-10

解决方案 5:

我更喜欢lockfile — 独立于平台的文件锁定

解决方案 6:

锁定是特定于平台和设备的,但通常,您有以下几种选择:

  1. 使用 flock() 或等效方法(如果您的操作系统支持它)。这是建议性锁定,除非您检查锁定,否则它会被忽略。

  2. 使用锁定-复制-移动-解锁方法,复制文件,写入新数据,然后移动它(移动,而不是复制 - 移动是 Linux 中的原子操作 - 检查您的操作系统),并检查锁定文件是否存在。

  3. 使用目录作为“锁”。如果你正在写入 NFS,这是必要的,因为 NFS 不支持 flock()。

  4. 进程之间也可以使用共享内存,但我从未尝试过;它是特定于操作系统的。

对于所有这些方法,您都必须使用自旋锁(失败后重试)技术来获取和测试锁。这确实会为错误同步留下一个小窗口,但它通常足够小,不会成为大问题。

如果您正在寻找跨平台的解决方案,那么最好通过其他机制登录到另一个系统(其次是上面的 NFS 技术)。

请注意,sqlite 受到与普通文件相同的 NFS 限制,因此您无法写入网络共享上的 sqlite 数据库并免费获得同步。

解决方案 7:

下面是一个如何使用filelock库的示例,它与Evan Fosmark 的实现类似:

from filelock import FileLock

lockfile = r"c:scr.txt"
lock = FileLock(lockfile + ".lock")
with lock:
    file = open(path, "w")
    file.write("123")
    file.close()

块内的任何代码with lock:都是线程安全的,这意味着它将在另一个线程访问该文件之前完成。

解决方案 8:

在操作系统级别协调对单个文件的访问充满了各种您可能不想解决的问题。

最好的办法是有一个单独的进程来协调对该文件的读/写访问。

解决方案 9:

锁定文件通常是特定于平台的操作,因此您可能需要考虑在不同的操作系统上运行的可能性。例如:

import os

def my_lock(f):
    if os.name == "posix":
        # Unix or OS X specific locking here
    elif os.name == "nt":
        # Windows specific locking here
    else:
        print "Unknown operating system, lock unavailable"

解决方案 10:

我一直在处理类似这样的情况,即我在同一个目录/文件夹中运行同一个程序的多个副本并记录错误。我的方法是在打开日志文件之前将“锁定文件”写入磁盘。程序在继续之前会检查“锁定文件”是否存在,如果“锁定文件”存在,则等待轮到它。

以下是代码:

def errlogger(error):

    while True:
        if not exists('errloglock'):
            lock = open('errloglock', 'w')
            if exists('errorlog'): log = open('errorlog', 'a')
            else: log = open('errorlog', 'w')
            log.write(str(datetime.utcnow())[0:-7] + ' ' + error + '
')
            log.close()
            remove('errloglock')
            return
        else:
            check = stat('errloglock')
            if time() - check.st_ctime > 0.01: remove('errloglock')
            print('waiting my turn')

编辑---在考虑了上面关于陈旧锁的一些评论后,我编辑了代码以添加对“锁定文件”陈旧的检查。在我的系统上对该函数进行几千次迭代计时,得出的平均时间为 0.002066...秒,就在之前:

lock = open('errloglock', 'w')

至之后:

remove('errloglock')

所以我认为我将从 5 倍的量开始,以指示陈旧程度并监控问题的情况。

此外,当我在处理时间问题时,我意识到有一些代码并不是真正必要的:

lock.close()

这是我在公开声明之后立即得到的,所以我在此编辑中将其删除。

解决方案 11:

这对我有用:不要占用大文件,分成几个小文件,创建文件 Temp,删除文件 A,然后将文件 Temp 重命名为 A。

import os
import json

def Server():
    i = 0
    while i == 0:
        try:        
                with open(File_Temp, "w") as file:
                    json.dump(DATA, file, indent=2)
                if os.path.exists(File_A):
                    os.remove(File_A)
                os.rename(File_Temp, File_A)
                i = 1
        except OSError as e:
                print ("file locked: " ,str(e))
                time.sleep(1)
            
            
def Clients():
    i = 0
    while i == 0:
        try:
            if os.path.exists(File_A):
                with open(File_A,"r") as file:
                    DATA_Temp = file.read()
            DATA = json.loads(DATA_Temp)
            i = 1
        except OSError as e:
            print (str(e))
            time.sleep(1)

解决方案 12:

场景是这样的:用户请求文件执行某项操作。然后,如果用户再次发送相同的请求,它会通知用户,在第一个请求完成之前,第二个请求不会完成。这就是为什么我使用锁机制来处理这个问题

这是我的工作代码:

from lockfile import LockFile
lock = LockFile(lock_file_path)
status = ""
if not lock.is_locked():
    lock.acquire()
    status = lock.path + ' is locked.'
    print status
else:
    status = lock.path + " is already locked."
    print status

return status

解决方案 13:

我从 grizzled-python 找到了一个简单且有效 (!) 的实现。

简单使用 os.open(..., O_EXCL) + os.close() 在 Windows 上不起作用。

解决方案 14:

您可能会发现pylocker非常有用。它可用于锁定文件或一般锁定机制,并且可以同时从多个 Python 进程访问。

如果您只是想锁定文件,请按照以下步骤操作:

import uuid
from pylocker import Locker

#  create a unique lock pass. This can be any string.
lpass = str(uuid.uuid1())

# create locker instance.
FL = Locker(filePath='myfile.txt', lockPass=lpass, mode='w')

# aquire the lock
with FL as r:
    # get the result
    acquired, code, fd  = r

    # check if aquired.
    if fd is not None:
        print fd
        fd.write("I have succesfuly aquired the lock !")

# no need to release anything or to close the file descriptor, 
# with statement takes care of that. let's print fd and verify that.
print fd

解决方案 15:

如果您只需要 Mac/POSIX,则无需外部包即可工作。

import sys
import stat
import os


filePath = "<PATH TO FILE>"
if sys.platform == 'darwin':
  flags = os.stat(filePath).st_flags
  if flags & ~stat.UF_IMMUTABLE:
    os.chflags(filePath, flags & stat.UF_IMMUTABLE)

如果你想解锁一个文件,只需改变,

  if flags & stat.UF_IMMUTABLE:
    os.chflags(filePath, flags & ~stat.UF_IMMUTABLE)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1355  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   8  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   9  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用