为什么文件的并发副本没有失败?

时间:2016-02-24 14:54:06

标签: python shutil

我有一个简单的脚本,我认为它会失败。 5个进程正在尝试将相同的大文件复制(使用shutil.copy)到相同的命运(具有相同的名称)。我想这会失败,因为其中一个进程将打开文件写入其中,其余进程将因文件已打开而失败。但这并没有发生,所有过程都正确结束。我还制作了复制文件的md5,看起来没问题。

我想知道这是一种稳定的行为,还是我做错了什么或做错事,以避免将来出现错误。

我还尝试了与另一个复制功能相同的脚本。使用win32file.CopyFile脚本会按预期失败(四个进程会出现异常:进程无法访问该文件,因为该文件正被另一个进程使用。)。

所有测试均在Windows上进行。

在这里,您可以找到我正在使用的脚本:

import os
import shutil
import hashlib
import win32file
import multiprocessing
from datetime import datetime

import logging
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

log.addHandler(ch)


NUM_PROCESS = 5
FILE = r"C:\Test\test.txt"


def worker(full_path):
    try:
        worker_name = multiprocessing.current_process().name

        std_path = full_path
        std_dst = r"C:\Test\download\test.txt"

        log.debug("[%s][%s] Copying to: %s to %s", str(datetime.now()), worker_name, std_path, std_dst)
        shutil.copy(std_path, std_dst)
        # win32file.CopyFile(std_path, std_dst, False)
        log.debug("[%s][%s] End copy", str(datetime.now()), worker_name)

        log.info("[%s][%s] Copy hash: %s" % (str(datetime.now()), worker_name, hashfile(open(std_dst, 'rb'),
                                                                                        hashlib.md5())))
        return
    except Exception as e:
        log.error("Can't copy file %s to %s. %s" % (std_path, std_dst, e))


def createEmptyFile(full_path, size):
    try:
        with open(full_path, "wb") as f:
            step = size / 1024 + 1
            if step < 2:
                step = 2
            for _ in xrange(1, step):
                f.write("\0" * 1024)

    except Exception as e:
        raise Exception("Error creating empty file: %s" % e)


def hashfile(afile, hasher, blocksize=65536):
    buf = afile.read(blocksize)
    while len(buf) > 0:
        hasher.update(buf)
        buf = afile.read(blocksize)
    return hasher.hexdigest()


if __name__ == '__main__':
    path = FILE
    os.chdir(os.path.dirname(path))

    if not os.path.exists(FILE):
        log.info("Creating test file")
        createEmptyFile(path, 9589934595)

    log.debug("Creating file hash")
    log.info("Origin hash: %s" % hashfile(open(path, 'rb'), hashlib.md5()))

    pool = multiprocessing.Pool(NUM_PROCESS)
    result = pool.map(worker, [path] * NUM_PROCESS)

1 个答案:

答案 0 :(得分:3)

shutil.copy函数使用shutil.copyfile函数,它使用简单的open()see sources

根据open()函数的documentation,如果文件已经存在,它将截断该文件。所以open()允许同时写入文件,它不会像你期望的那样锁定它。

当最后一个工作程序执行open()函数时,它会截断文件,而其他工作程序继续写入。最后一个worker添加它可能截断的数据,然后覆盖所有其他工作者写的数据。当第一个工作人员完成复制文件时已经复制了所有数据,因此复制文件的计算哈希值应与原始文件相同。保留工作人员只是覆盖现有数据,但文件不会改变其哈希值。

如果要锁定文件以进行复制,则应自行使用flock()