26

在基于 Debian 的操作系统(Ubuntu、Debian Squeeze)上,我使用 Python(2.7、3.2)fcntl 来锁定文件。据我了解,fnctl.flock 以某种方式锁定文件,如果另一个客户端想要锁定同一个文件,则会引发异常。

我构建了一个小例子,我希望会抛出一个异常,因为我首先锁定了文件,然后,紧接着,我再次尝试锁定它:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import fcntl
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX)
try:
    fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
    print("can't immediately write-lock the file ($!), blocking ...")
else:
    print("No error")

但是该示例仅打印“无错误”。

如果我将此代码拆分为同时运行的两个客户端(一个锁定然后等待,另一个在第一个锁定已经激活后尝试锁定),我会得到相同的行为 - 完全没有效果。

这种行为的解释是什么?

编辑

根据 nightcracker 的要求进行更改,此版本也打印“无错误”,尽管我不希望这样:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import fcntl
import time
fcntl.flock(open('/tmp/locktest', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
try:
    fcntl.flock(open('/tmp/locktest', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
    print("can't immediately write-lock the file ($!), blocking ...")
else:
    print("No error")
4

7 回答 7

18

旧帖子,但如果其他人找到它,我会得到这种行为:

>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX)
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
# That didn't throw an exception

>>> f = open('test.flock', 'w')
>>> fcntl.flock(f, fcntl.LOCK_EX)
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 35] Resource temporarily unavailable
>>> f.close()
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
# No exception

看起来在第一种情况下,文件在第一行之后被关闭,大概是因为文件对象不可访问。关闭文件会释放锁。

于 2013-04-10T09:52:38.690 回答
16

我遇到了同样的问题......我已经解决了它,将打开的文件保存在一个单独的变量中:

不会工作:

fcntl.lockf(open('/tmp/locktest', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)

作品:

lockfile = open('/tmp/locktest', 'w')
fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)

我认为第一个不起作用,因为打开的文件被垃圾收集关闭锁释放

于 2013-06-29T01:30:05.650 回答
10

知道了。我的脚本中的错误是我在每次调用时创建一个新的文件描述符:

fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)
(...)
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)

相反,我必须将文件对象分配给一个变量,而不是尝试锁定:

f = open('/tmp/locktest', 'r')
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
(...)
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)

比我还得到我想看到的异常:IOError: [Errno 11] Resource temporarily unavailable. 现在我必须考虑在哪些情况下使用 fcntl 才有意义。

于 2012-03-28T13:56:56.743 回答
6

有两个陷阱。根据文档

  1. 当运算为LOCK_SHorLOCK_EX时,也可以进行按位或运算,LOCK_NB以避免阻塞获取锁。如果LOCK_NB使用 并且无法获取锁,IOError则会引发 an 并且异常将具有errno设置为EACCESor的属性EAGAIN(取决于操作系统;为了可移植性,请检查这两个值)。

    你忘了设置LOCK_NB

  2. 至少在某些系统上,LOCK_EX只能在文件描述符引用打开用于写入的文件时使用。

    您打开了一个文件以供阅读,您的系统可能不支持该文件LOCK_EX

于 2012-03-28T12:44:26.887 回答
3

您可以参考这篇文章了解不同锁定方案的更多详细信息。
至于您的第二个问题,请使用fcntl来锁定不同的进程(lockf为简单起见,请改为使用)。在linuxlockf上只是一个包装器fcntl,两者都与(pid, inode)pair相关联。
1. 用于fcntl.fcntl提供跨进程的文件锁。

import os
import sys
import time
import fcntl
import struct


fd = open('/etc/mtab', 'r')
ppid = os.getpid()
print('parent pid: %d' % ppid)
lockdata = struct.pack('hhllh', fcntl.F_RDLCK, 0, 0, 0, ppid)
res = fcntl.fcntl(fd.fileno(), fcntl.F_SETLK, lockdata)
print('put read lock in parent process: %s' % str(struct.unpack('hhllh', res)))
if os.fork():
    os.wait()
    lockdata = struct.pack('hhllh', fcntl.F_UNLCK, 0, 0, 0, ppid)
    res = fcntl.fcntl(fd.fileno(), fcntl.F_SETLK, lockdata)
    print('release lock: %s' % str(struct.unpack('hhllh', res)))
else:
    cpid = os.getpid()
    print('child pid: %d' % cpid)
    lockdata = struct.pack('hhllh', fcntl.F_WRLCK, 0, 0, 0, cpid)
    try:
        fcntl.fcntl(fd.fileno(), fcntl.F_SETLK, lockdata)
    except OSError:
        res = fcntl.fcntl(fd.fileno(), fcntl.F_GETLK, lockdata)
        print('fail to get lock: %s' % str(struct.unpack('hhllh', res)))
    else:
        print('succeeded in getting lock')

2. 使用fcntl.lockf

import os
import time
import fcntl

fd = open('/etc/mtab', 'w')
fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
if os.fork():
    os.wait()
    fcntl.lockf(fd, fcntl.LOCK_UN)
else:
    try:
        fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError as e:
        print('failed to get lock')
    else:
        print('succeeded in getting lock')
于 2018-10-08T09:38:34.470 回答
0

您需要传入文件描述符(可通过调用文件对象的 fileno() 方法获得)。当相同的代码在单独的解释器中运行时,下面的代码会引发 IOError。

>>> import fcntl
>>> thefile = open('/tmp/testfile')
>>> fd = thefile.fileno()
>>> fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
于 2012-03-28T12:45:13.797 回答
-1

尝试:

global f
f = open('/tmp/locktest', 'r')

当文件关闭时,锁将消失。

于 2014-08-14T08:03:05.937 回答