我正在尝试将大量文件从一个目录复制到另一个目录。但是,在尝试通过使用Threading加快处理速度时,出现错误,提示它打开文件过多。目前,文件的测试批次约为700多个,下面是代码。我该如何解决?在我的示例中,我将文件从网络上的一个位置复制到同一网络上的另一个位置,文件范围从1mb到100mb。
def copy_file_to_directory(file, directory):
'''
Description:
Copies the file to the supplied directory if it exists
'''
if os.path.isfile(file):
url = os.path.join(directory, os.path.basename(file))
try:
shutil.copyfile(file, url)
shutil.copystat(file, url)
return True
except IOError as e:
print (e)
return False
def copy_files_to_directory(files, directory):
'''
Directory:
Copy a list of files to directory, overwriting existing files
'''
if not os.path.isdir(directory):
os.makedirs(directory)
if not os.path.isdir(directory):
return False
workers = []
for x in files:
if os.path.isfile(x):
worker = threading.Thread(target=copy_file_to_directory, args=(x,directory))
workers.append(worker.start())
# wait until they are all done processing
for x in workers:
x.join()
return True
files = [] # list of files
copy_files_to_directory(files, 'C:/Users/John')
答案 0 :(得分:3)
几乎可以肯定,您不想让每个文件产生一个线程。在一定程度上,线程化可以为您带来好处(并且您不只是饱和磁盘I / O带宽),您可能应该仅使用具有固定线程数的线程池(例如,并发.futures.ThreadPoolExecutor)。这将限制一次打开的文件数。实际上,这种情况在Python文档中作为示例给出:series of articles
使它适合您的使用:
with ThreadPoolExecutor(max_workers=4) as e:
for x in files:
if os.path.isfile(x):
e.submit(copy_file_to_directory, x, directory)
答案 1 :(得分:1)
我对线程池(4个线程池和8个线程池),直接shutil
和文件的OS副本(即,不在Python中)进行了一些计时。
目标设备是以下设备之一:
源设备是非常快的Mac内部SSD,能够进行8K视频编辑,比任何目标设备都快得多。
首先创建100个介于1 MB和100MB之间的随机数据文件:
#!/bin/bash
cd /tmp/test/src # a high bandwidth source SSD
for fn in {1..100}.tgt
do
sz=$(( (1 + RANDOM % 100)*1000*1000 ))
printf "creating %s with %s MB\n" "$fn" $((sz/(1000*1000) ))
head -c "$sz" </dev/urandom >"$fn"
done
现在计时码:
import shutil
import os
import pathlib
import concurrent.futures
import random
def copy_file_to_directory(file, directory):
'''
Description:
Copies the file to the supplied directory if it exists
'''
if os.path.isfile(file):
url = os.path.join(directory, os.path.basename(file))
try:
shutil.copyfile(file, url)
shutil.copystat(file, url)
return True
except IOError as e:
print (e)
return False
def f1(files, directory):
'''
Directory:
Copy a list of files to directory, overwriting existing files
'''
if not os.path.isdir(directory):
os.makedirs(directory)
if not os.path.isdir(directory):
return False
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as e:
for x in files:
if os.path.isfile(x):
e.submit(copy_file_to_directory, x, directory)
return True
def f2(files, directory):
'''
Directory:
Copy a list of files to directory, overwriting existing files
'''
if not os.path.isdir(directory):
os.makedirs(directory)
if not os.path.isdir(directory):
return False
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as e:
for x in files:
if os.path.isfile(x):
e.submit(copy_file_to_directory, x, directory)
return True
def f3(files, p):
'''
Serial file copy using copy_file_to_directory one file at a time
'''
for f in files:
if os.path.isfile(f):
copy_file_to_directory(f, p)
if __name__=='__main__':
import timeit
src='/tmp/test/src'
cnt=0
sz=0
files=[]
for fn in pathlib.Path(src).glob('*.tgt'):
sz+=pathlib.Path(fn).stat().st_size
cnt+=1
files.append(fn)
print('{:,.2f} MB in {} files'.format(sz/(1000**2),cnt))
for case, tgt in (('Local spinning drive','/Volumes/LaCie 2TB Slim TB/test'),('local SSD','/Volumes/SSD TM/test'),('smb net drive','/Volumes/andrew/tgt-DELETE')):
print("Case {}=> {}".format(case,tgt))
for f in (f1,f2,f3):
print(" {:^10s}{:.4f} secs".format(f.__name__, timeit.timeit("f(files, tgt)", setup="from __main__ import f, files, tgt", number=1)))
结果是:
4,740.00 MB in 100 files
Case Local spinning drive=> /Volumes/LaCie 2TB Slim TB/test
f1 56.7113 secs
f2 71.2465 secs
f3 46.2672 secs
Case local SSD=> /Volumes/SSD TM/test
f1 9.7915 secs
f2 10.2333 secs
f3 10.6059 secs
Case smb net drive=> /Volumes/andrew/tgt-DELETE
f1 41.6251 secs
f2 40.9873 secs
f3 51.3326 secs
并与原始unix复制时间进行比较:
$ time cp /tmp/test/src/*.* "/Volumes/LaCie 2TB Slim TB/test"
real 0m41.127s
$ time cp /tmp/test/src/*.* "/Volumes/SSD TM/test"
real 0m9.766s
$ time cp /tmp/test/src/*.* "/Volumes/andrew/tgt-DELETE"
real 0m49.993s
我怀疑,时间(至少对于MY测试)大致相同,因为限制速度是底层I / O带宽。网络设备的线程池具有一些优点,但在机械驱动器上却有很大的缺点。
这些结果仅用于从文件的一个同质位置复制到另一同质位置,而不处理单个文件。如果这些步骤涉及每个文件的某些CPU密集型功能,或者单个文件的目标涉及不同的I / O路径(即,一个文件到SSD,并且根据某种情况下一个文件到网络等),则可能赞成使用并发方法。