用Python锁定一个文件

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

好的,所以我最终用我在这里写的代码,在我的网站上 ( 也可以在GitHub上 )。 我可以用下面的方式使用它:

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

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

尽管如Kevin所说,尽可能地避免从多个进程写入文件。

如果你可以把你的问题放到数据库中,你可以使用SQLite。 它支持并发访问并处理自己的锁定。

我更喜欢lockfile – 与平台无关的文件锁定

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

你最好的选择是有一个单独的进程,协调读/写访问该文件。

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

  1. 使用flock()或equivilent(如果你的操作系统支持它)。 这是建议性锁定,除非您检查锁定,否则忽略。
  2. 使用锁复制移动解锁方法,复制文件,写入新数据,然后移动它(移动,而不是复制 – 移动是Linux中的一个原子操作 – 检查您的操作系统),然后检查存在锁定文件。
  3. 使用一个目录作为“锁”。 如果你正在写入NFS,这是必须的,因为NFS不支持flock()。
  4. 在进程之间也有使用共享内存的可能性,但是我从来没有尝试过; 它是非常特定的。

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

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

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

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

 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" 

我一直在寻找几个解决方案来做到这一点,我的选择是oslo.concurrency

这是强大的,相对良好的文件。 它基于紧固件。

其他解决方案

  • Portalocker :需要pywin32,这是一个exe安装,所以不可能通过pip
  • 紧固件 :记录不完善
  • lockfile :已弃用
  • flufl.lock :POSIX系统的NFS安全文件锁定。
  • simpleflock :上次更新2013-07
  • zc.lockfile :最新更新2016-06(截至2017-03)
  • lock_file :在2007-10的最后一次更新

我发现一个简单的工作(!) 实施从灰熊 – 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() 

我刚才在公开声明之后,所以我已经在这个编辑中删除它。

情况是这样的:用户请求一个文件做一些事情。 然后,如果用户再次发送相同的请求,它会通知用户第二个请求没有完成,直到第一个请求完成。 这就是为什么我使用锁机制来处理这个问题。

这是我的工作代码:

 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.) import fcntl def lock_file(f): fcntl.lockf(f, fcntl.LOCK_EX) def unlock_file(f): pass except ModuleNotFoundError: # Windows file locking import msvcrt 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 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 # Allows users to use the 'close' function if they want, in case # the user did not have the AtomicOpen in a "with" block. def close(self): self.__exit__() # Unlock the file and close the file object def __exit__(self, exc_type=None, exc_value=None, traceback=None): # 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”可以用在任何通常使用“open”语句的地方。

警告:如果在调用退出之前在Windows和Python上运行,我不确定锁的行为是什么。

警告:这里提供的锁定是建议性的,不是绝对的。 所有潜在的竞争进程必须使用“AtomicOpen”类。

你可能会发现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 
Interesting Posts