python - 当多个进程尝试同时写入文件然后从文件读取时如何防止竞争条件

标签 python io race-condition python-import python-os

我有以下代码(为清楚起见进行了简化):

import os
import errno
import imp


lib_dir = os.path.expanduser('~/.brian/cython_extensions')
module_name = '_cython_magic_5'
module_path = os.path.join(lib_dir, module_name + '.so')
code = 'some code'

have_module = os.path.isfile(module_path)
if not have_module:
    pyx_file = os.path.join(lib_dir, module_name + '.pyx')

    # THIS IS WHERE EACH PROCESS TRIES TO WRITE TO THE FILE.  THE CODE HERE 
    # PREVENTS A RACE CONDITION.
    try:
        fd = os.open(pyx_file, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
    except OSError as e:
        if e.errno == errno.EEXIST:
            pass
        else:
            raise
    else:
        os.fdopen(fd, 'w').write(code)

# THIS IS WHERE EACH PROCESS TRIES TO READ FROM THE FILE.  CURRENTLY THERE IS A
# RACE CONDITION.
module = imp.load_dynamic(module_name, module_path)

(以上部分代码借鉴自this answer。)

当同时运行多个进程时,此代码只会导致一个进程打开并写入 pyx_file(假设 pyx_file 尚不存在)。问题是,当这个进程正在写入 pyx_file 时,其他进程试图加载 pyx_file —— 在后面的进程中会出现错误,因为在它们读取 的时候code>pyx_file,不完整。 (具体来说,引发了 ImportError,因为进程正在尝试导入文件的内容。)

避免这些错误的最佳方法是什么?一种想法是让进程在 while 循环中不断尝试导入 pyx_file,直到导入成功。 (这个解决方案似乎不是最优的。)

最佳答案

这样做的方法是每次打开都拿一个独占锁。写入者在写入数据时持有锁,而读取者阻塞直到写入者通过 fdclose 调用释放锁。如果文件已部分写入并且写入过程异常退出,这当然会失败,因此如果无法加载模块,则应显示删除文件的适当错误:

import os
import fcntl as F

def load_module():
    pyx_file = os.path.join(lib_dir, module_name + '.pyx')

    try:
        # Try and create/open the file only if it doesn't exist.
        fd = os.open(pyx_file, os.O_CREAT | os.O_EXCL | os.O_WRONLY):

        # Lock the file exclusively to notify other processes we're writing still.
        F.flock(fd, F.LOCK_EX)
        with os.fdopen(fd, 'w') as f:
            f.write(code)

    except OSError as e:
        # If the error wasn't EEXIST we should raise it.
        if e.errno != errno.EEXIST:
            raise

    # The file existed, so let's open it for reading and then try and
    # lock it. This will block on the LOCK_EX above if it's held by
    # the writing process.
    with file(pyx_file, "r") as f:
        F.flock(f, F.LOCK_EX)

    return imp.load_dynamic(module_name, module_path)

module = load_module()

关于python - 当多个进程尝试同时写入文件然后从文件读取时如何防止竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30407352/

相关文章:

c# - 线程启动期间的竞争条件?

c - 操作系统设计竞争条件

java - 我如何在 Java 中实现这个 Python 片段?

C 文件 I/O 问题

io - 二进制文件I/O

java - file.mkdirs() 不起作用

multithreading - 为什么这段代码没有达到竞争条件?

python - 遍历决策树并捕获每个节点

Python 和 sqlite3 - 将文本文件导入数据库

python - 语料库中每个文本的平均句子长度(python3 和 nltk)