2009-01-28 116 views
105

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

回答

5

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

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" 
+5

您可能已经知道这一点,但平台模块也可用于获取有关运行平台的信息。 platform.system()。 http://docs.python.org/library/platform.html。 “ – monkut 2009-01-29 00:54:14

8

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

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

+15

”单独的进程,坐标读取/写入访问该文件“ - 换句话说,实现数据库服务器:-) – 2009-01-31 08:39:10

+1

这实际上是最好的答案。仅仅说“使用数据库服务器”就过于简单了,因为db并不总是成为正确的工具。如果它需要是纯文本文件呢?一个好的解决方案可能是产生一个子进程,然后通过命名管道,unix套接字或共享内存访问它。 – 2011-07-22 04:55:24

+7

-1因为这只是FUD而没有解释。锁定一个文件以供写入对我来说似乎是一个非常简单的概念,操作系统提供了像``flock`](http://linux.die.net/man/2/flock)这样的函数。 “滚动自己的互斥体和守护进程来管理它们”似乎是一种相当极端和复杂的方法来解决......一个你实际上没有告诉我们的问题,但只是存在一些可疑的暗示。 – 2016-05-10 11:38:28

30

有一个跨平台的文件锁定模块的位置:Portalocker

虽然凯文说,写从多个进程同时文件是你想避免,如果在所有可能的东西。

如果您可以将您的问题解决到数据库中,则可以使用SQLite。它支持并发访问并处理自己的锁定。

+11

+1 - SQLite几乎总是在这种情况下走的路。 – cdleary 2009-01-29 05:38:28

+2

Portalocker需要Windows的Python扩展。 – n611x007 2013-02-21 09:59:08

+1

@naxa有一个它只依赖于msvcrt和ctypes的变体,请参阅http://roundup.hg.sourceforge.net/hgweb/roundup/roundup/file/tip/roundup/backends/portalocker.py – 2013-04-15 21:21:40

9

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

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

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

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

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

1

我发现了一个简单的工作(!)来自grizzled-python的implementation

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

1

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

这里是代码:“锁定文件”

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() 

,我曾紧随公开声明,所以我已删除了它在这个编辑中。

5

我一直在寻找多种解决方案做到这一点,我选择一直 oslo.concurrency

它的强大和相对有据可查。它基于紧固件。

其他的解决方案:

0

最新更新你会发现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 
1

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

这是我工作的代码:

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 
2

其他的解决方案列举了大量的外部代码库。如果您更愿意自己动手​​,那么下面是一些跨平台解决方案的代码,它使用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”类。