在Python中锁定文件

在Python中锁定文件,python,file-locking,Python,File Locking,我需要锁定一个用Python编写的文件。它将一次从多个Python进程访问。我在网上找到了一些解决方案,但大多数都无法达到我的目的,因为它们通常只基于Unix或Windows。锁定文件通常是特定于平台的操作,因此您可能需要考虑在不同的操作系统上运行的可能性。例如: import os def my_lock(f): if os.name == "posix": # Unix or OS X specific locking here elif os.name =

我需要锁定一个用Python编写的文件。它将一次从多个Python进程访问。我在网上找到了一些解决方案,但大多数都无法达到我的目的,因为它们通常只基于Unix或Windows。

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

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"

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


您最好使用一个单独的进程来协调对该文件的读/写访问。

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

虽然正如Kevin所说,如果可能的话,要避免同时从多个进程写入文件


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

锁定是特定于平台和设备的,但通常有几个选项:

  • 使用flock(),或等效工具(如果您的操作系统支持)。这是建议锁定,除非您检查锁,否则它将被忽略
  • 使用lock-copy-move-unlock方法,复制文件,写入新数据,然后移动它(move,而不是copy-move在Linux中是一个原子操作——检查您的操作系统),然后检查锁文件是否存在
  • 将目录用作“锁”。如果要写入NFS,这是必需的,因为NFS不支持flock()
  • 也有可能在进程之间使用共享内存,但我从未尝试过;它非常特定于操作系统
  • 对于所有这些方法,您必须使用自旋锁(失败后重试)技术来获取和测试锁。这确实为错误同步留下了一个很小的窗口,但它通常很小,不会成为一个大问题

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


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

    好的,因此我最终使用了我编写的代码()。我可以按以下方式使用它:

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

    我更喜欢-独立于平台的文件锁定我从灰色python中找到了一个简单且有效的(!)


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

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

    代码如下:

    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 + '\n')
                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()
    

    我在open语句之后立即将其删除,因此我在本次编辑中将其删除。

    我一直在寻找几种解决方案,我的选择是

    它功能强大,文档记录相对完整。它是以紧固件为基础的

    其他解决办法:

    • :需要pywin32,这是一个exe安装,因此无法通过pip进行安装
    • :记录不良
    • :弃用
    • :用于POSIX系统的NFS安全文件锁定
    • :最后更新日期2013-07
    • :最新更新2016-06(截至2017-03)
    • :上次更新于2007-10
      • 您可能会发现这非常有用。通常,它可以用来锁定文件或锁定机制,并且可以一次从多个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
        

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

        这是我的工作代码:

        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
        

        其他解决方案引用了大量外部代码库。如果您愿意自己做,下面是一些跨平台解决方案的代码,该解决方案在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上运行时在调用exit之前崩溃,我不确定锁的行为会是什么
        • 这里提供的锁定是建议性的,而不是绝对的。所有可能相互竞争的进程都必须使用“AtomicOpen”类
        • 截至(2020年11月9日),此代码仅锁定Posix系统上的可写文件。在发布后的某个时间点,在此日期之前,在只读文件上使用
          fcntl.lock
          是非法的
        要添加到,下面是一个如何使用的示例:

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

        这对我来说很有效: 不要占用大文件,将其分为多个小文件 创建文件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)
        

        您可能已经知道这一点,但平台模块也可用于获取有关
        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)