如何使用'with'从上下文管理器获取“结果”

时间:2014-04-23 20:54:44

标签: python with-statement filelock contextmanager

我找到了一个文件锁定类的演示(这里:https://groups.google.com/forum/#!topic/pug-pe/mQr7KX-cenU),但我不太了解使用它的机制。

@contextmanager
def FileLock(lock_file):
    if os.path.exists(lock_file):
        print 'Only one script can run at once. '\
              'Script is locked with %s' % lock_file
        sys.exit(-1)
    else:
        open(lock_file, 'w').write("1")
        try:
            yield
        finally:
            os.remove(lock_file)

我相信这说“如果传入的文件不存在,请将其打开。完成后,将其删除。

证明的用途是:

with FileLock('/tmp/my_script.lock'):
    print "I am here and I am sleeping for 10 sec..."
    time.sleep(10)

它工作正常 - 如果我运行脚本一次,我看到“我在这里,我在睡觉10秒......”,如果我在10秒内再次运行它,我看到“只有一个脚本可以立即运行。脚本用/tmp/my_script.lock锁定“。但是,要使用文件锁定,通常需要在执行操作之前“等到获得锁定”。但是,'sys.exit()'似乎阻止了这一点。似乎我想以某种方式将'with'包装在while循环中?类似的东西:

while fileIsLocked:
    with FileLock('/tmp/my_script.lock'): # try to get the lock
        print "I am here and I am sleeping for 10 sec..."
        time.sleep(10)

但我不明白如何从FileLock获取返回值。有人可以解释一下如何做到这一点吗?

2 个答案:

答案 0 :(得分:3)

您应该使用以下内容:

@contextmanager
def FileLock(lock_file):
    while os.path.exists(lock_file):
        print 'Only one script can run at once. '\
              'Script is locked with %s' % lock_file
        time.sleep(1)
    open(lock_file, 'w').write("1")
    try:
        yield
    finally:
        os.remove(lock_file)

这直接回答了声明的需求。 OTOH方法非常错误,因为在检查文件存在和打开文件之间存在明确的竞争条件。更稳定的方法应该使用O_EXCL来确保文件在创建期间不存在,或flock使用锁定文件内容,而不是它的存在。此外,可以使用一些内核级IPC(Posix信号量,SysV信号量等)

答案 1 :(得分:0)

我已经接受了Netch的答案,因为它都证明了我声明的解决方案'需要,并建议更好的解决方案。我已经在这里发布了使用flock的完整性。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import fcntl
import time

try:
  f = open('/tmp/locktest', 'w')
  fcntl.flock(f, fcntl.LOCK_EX) # Get an exclusive lock - this will block until it gets the lock
  print "Sleeping..."
  time.sleep(10)
except IOError:
  print("can't immediately write-lock the file ($!), blocking ...")
else:
  print("No error")

  print("End of file")