寻找可靠的python进程同步技术(Linux非便携)

Posted

技术标签:

【中文标题】寻找可靠的python进程同步技术(Linux非便携)【英文标题】:In search of reliable python process synchronization techniques (Linux non-portable) 【发布时间】:2016-08-19 00:18:41 【问题描述】:

[我很难在 Linux 上使用 python 3 实现线程/进程安全的解决方案来获取文件锁(我不关心可移植的解决方案,因为我正在开发的程序广泛使用Linux 内核专有容器化技术)。]

阅读http://apenwarr.ca/log/?m=201012#13后,我决定使用fcntl.lockf()锁定一个文件以供进程独占访问,并编写了以下函数:

import contextlib as Contextlib
import errno as Errno
import fcntl as Fcntl
import os as Os


@Contextlib.contextmanager
def exclusiveOpen(filename,
                  mode):
  try:
    fileDescriptor = Os.open(filename,
                             Os.O_WRONLY | Os.O_CREAT)
  except OSError as e:
    if not e.errno == Errno.EEXIST:
      raise

  try:
    Fcntl.lockf(fileDescriptor,
                Fcntl.LOCK_EX)
    fileObject = Os.fdopen(fileDescriptor,
                           mode)

    try:
      yield fileObject
    finally:
      fileObject.flush()
      Os.fdatasync(fileDescriptor)
  finally:
    Os.close(fileDescriptor)

除此之外,我确定它是不正确的(为什么它不阻止Fcntl.lockf(fileDescriptor, Fcntl.LOCK_EX)?),最让我感到不安的部分是获取fileDescriptor 的位置 - 如果文件是不存在的,它被创建了......但是如果两个进程同时执行这部分,会发生什么?是否存在竞争条件,两个线程都尝试创建文件?如果是这样,人们怎么可能阻止这种情况 - 当然不是使用另一个锁定文件(?),因为它必须以相同的方式创建(?!?!)我迷路了。任何帮助是极大的赞赏。

更新:发布了解决潜在问题的另一种方法。我在这种方法中看到的问题是,过程名称不能等于现有 UNIX 域套接字的名称(可能由另一个程序创建) - 我对此是否正确?

【问题讨论】:

【参考方案1】:

基于https://***.com/a/7758075/5449837,我最终使用抽象的 UNIX 套接字而不是锁定文件来同步进程,(如果有人发布更好的答案,我很乐意接受)。

import contextlib as Contextlib
import os as Os
import socket as Socket
import time as Time


@Contextlib.contextmanager
def locked(name,
           timeout):
  try:
    lock = Socket.socket(Socket.AF_UNIX, 
                         Socket.SOCK_DGRAM)

    while True:
      try:
        lock.bind('\0' + name)
        break
      except:
        if timeout:
          Time.sleep(1)
          timeout = timeout - 1 
        else:
          raise

    yield
  except:
    raise
  finally:
    lock.close()


# usage:
with locked("procedureName", 5):
  pass # perform actions on shared resource

致反对者:想解释一下为什么你认为这很糟糕?

【讨论】:

不相关:1- 请不要重命名 stdlib 模块。另外,follow pep-8 naming conventions even for your own modules (in the public code) 2- 不要在答案中提出问题(这不是论坛)。如果您有问题; ask it as a question instead 谢谢(会解决这个问题):) - 出于好奇,为什么没有 1? 为什么?因为可读性很重要。如果我看到Socket;它看起来像一些自定义类。如果我看到socket,我认为它是一个标准库模块。没有必要就不需要制造摩擦 所以没有潜在的技术原因......对我来说,如果我可以将我的变量命名为“socket”而不是“socket_”,因为我将套接字作为 Socket 导入,例如【参考方案2】:

AFAIK,创建文件时没有可能的竞争条件。如果两个(或更多)进程或线程尝试通过open 使用O_CREAT 标志而不是O_EXCL 标志同时创建同一个文件,则该文件将被创建一次,并且所有调用者都将获得一个打开的文件描述符在同一个文件上 - 正是你需要的。我假设您在 Linux 上使用 C-Python,并且您的 Python 代码将使用底层 C open 函数结束。

因此,当您的代码执行lockf 函数时,您在现有文件上有一个打开的文件描述符,因此底层lockf 调用保证一次只有一个进程可以持有锁,前提是底层文件系统支持锁定。

我在 Unix (FreeBSD) 系统上尝试过它,它按预期工作。

【讨论】:

你的 FreeBSD 上的 lockf 是否阻塞? @MCH 好吧,我无法重现竞争条件,但我刚刚启动了 2 个进程,在两个进程中打开同一个文件(但使用 O_RDWR 而不是 O_RDONLY)并首先锁定.当我尝试在第二个中获取锁时,它会被阻止,直到第一个进程释放锁或关闭文件。一旦它获得了锁,它就会阻止任何其他试图获取它的进程。

以上是关于寻找可靠的python进程同步技术(Linux非便携)的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中进行跨进程跨脚本同步

Linux:线程同步

Linux的同步访问技术

linux内核同步问题

Linux线程及同步

Python 中的进程线程协程同步异步回调