You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python多进程并发创建目录遇文件已存在错误,求解决方案

解决Python脚本并行运行时的目录创建竞态条件问题

为什么1秒延迟没用?

你遇到的是典型的TOCTOU(检查时间与使用时间)竞态条件。os.path.exists(output_folder) 和 os.mkdir(output_folder) 这两个操作不是原子的——哪怕加了1秒sleep,也可能出现这种情况:第一个进程刚检查完目录不存在,还没来得及执行mkdir,操作系统就调度第二个进程运行,第二个进程同样检查到目录不存在,随后两个进程都尝试创建同一个目录,导致第二个进程抛出OSError: [Errno 17] File exists。

sleep只能降低竞态发生的概率,但无法彻底消除它,因为进程的执行时机完全由操作系统调度器控制,不是sleep能精准把控的。

最优解决方案

方案1:用异常捕获替代预检查(推荐,最简单)

直接尝试创建目录,捕获"文件已存在"的异常即可。因为os.mkdir在Unix-like系统上是原子操作——只要有一个进程成功创建了目录,其他进程的mkdir就会立刻抛出异常,不会出现两个进程都成功创建的情况。

修改你的create_output_directory函数:

import time

def create_output_directory():
    output_folder = 'output_%s' % datetime.now().strftime('%Y%m%d')
    try:
        os.mkdir(output_folder)
        logger.info('Created output directory %s' % output_folder)
    except OSError as e:
        if e.errno == 17:  # 捕获"文件已存在"的异常
            logger.warning('Previous directory %s exists, files may be overwritten' % output_folder)
        else:
            raise  # 其他异常重新抛出,不忽略
    
    # 处理软链接的竞态:循环重试直到成功
    while True:
        try:
            if os.path.exists('output_latest'):
                os.remove('output_latest')
            os.symlink(output_folder, 'output_latest')
            break
        except OSError as e:
            logger.debug('Failed to update output_latest symlink, retrying...')
            time.sleep(0.1)
    return output_folder

这里对软链接的操作加了循环重试,因为多个进程同时删除/创建软链接也可能出现竞态(比如进程A刚删除output_latest,进程B也重复删除一次,随后进程A创建软链接,进程B再创建就会报错),循环重试可以解决这个问题。

方案2:使用文件锁(更严谨,适合复杂场景)

如果需要确保只有一个进程能执行创建目录和更新软链接的操作,可以用文件锁实现互斥。比如创建一个临时锁文件,只有拿到锁的进程才能执行目录创建逻辑:

import fcntl

def create_output_directory():
    output_folder = 'output_%s' % datetime.now().strftime('%Y%m%d')
    # 创建锁文件(不存在则自动创建)
    lock_file = open('.directory_lock', 'w')
    try:
        # 获取排他锁,阻塞直到拿到锁
        fcntl.flock(lock_file, fcntl.LOCK_EX)
        
        # 现在只有当前进程能执行下面的逻辑
        if not os.path.exists(output_folder):
            os.mkdir(output_folder)
            logger.info('Created output directory %s' % output_folder)
        else:
            logger.warning('Previous directory %s exists, files may be overwritten' % output_folder)
        
        if os.path.exists('output_latest'):
            os.remove('output_latest')
        os.symlink(output_folder, 'output_latest')
    finally:
        # 释放锁并关闭文件
        fcntl.flock(lock_file, fcntl.LOCK_UN)
        lock_file.close()
    return output_folder

这种方式能彻底避免所有竞态,但需要注意:文件锁在Unix-like系统上有效,Windows系统需要用不同的锁机制(比如msvcrt.locking)。

总结

  • sleep无法解决竞态条件,只能降低概率;
  • 最简单的解决方案是用异常捕获+重试处理目录和软链接的创建;
  • 如果需要严格的互斥,使用文件锁是更稳妥的选择。

内容的提问来源于stack exchange,提问作者Ruggero Turra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:38:44