为什么文件的并发副本不会失败?
Why is concurrent copy of a file not failing?
我有一个简单的脚本,我认为它会失败。 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("[=10=]" * 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)
shutil.copy
函数使用 shutil.copyfile
函数,它使用简单的 open()
(see sources)
根据 documentation 的 open()
函数,如果文件已经存在,它将被截断。所以 open()
允许同时写入文件,它不会像您预期的那样锁定文件。
当最后一个worker执行open()
函数时,它会截断文件,而其他worker继续向前写入。最后一个工作人员添加它可能截断的数据,然后覆盖所有其他工作人员写入的数据。当第一个 worker 完成复制时,文件已经复制了所有数据,因此复制文件的计算哈希值应该与原始文件相同。剩余的工作人员只是覆盖现有数据,但文件不会更改其哈希值。
如果要锁定文件以供复制,您应该自己使用 flock()
。
我有一个简单的脚本,我认为它会失败。 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("[=10=]" * 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)
shutil.copy
函数使用 shutil.copyfile
函数,它使用简单的 open()
(see sources)
根据 documentation 的 open()
函数,如果文件已经存在,它将被截断。所以 open()
允许同时写入文件,它不会像您预期的那样锁定文件。
当最后一个worker执行open()
函数时,它会截断文件,而其他worker继续向前写入。最后一个工作人员添加它可能截断的数据,然后覆盖所有其他工作人员写入的数据。当第一个 worker 完成复制时,文件已经复制了所有数据,因此复制文件的计算哈希值应该与原始文件相同。剩余的工作人员只是覆盖现有数据,但文件不会更改其哈希值。
如果要锁定文件以供复制,您应该自己使用 flock()
。