在多任务方面,Python比java多了一个操作进程。故而,在此用python的多进程实现一下文件夹的复制,这一功能用多线程自然也能实现。
由于不知道要复制的文件夹里有多少文件,因此一个一个用multiprocessing.Process创建进程就不合适了。所以使用进程池来管理任务进程, 同时用任务队列保存需要拷贝的文件
先构造全局的任务队列,然后在入口方法里初始化
import os, multiprocessing
q = None
...
def main():
...
global q
q = multiprocessing.Manager().Queue() # 注意一定是Manager()的Queue(),否则子进程执行不起来
...
然后让用户输入要复制的文件夹的绝对路径,以及目标文件夹路径,并进行合法性判断(此处写死)
dirName = "D:\\szcPython"
newDirName = "D:\\test"
if not os.path.isdir(dirName):
print("无此文件夹")
return
if os.path.exists(newDirName):
print("目标文件夹已存在")
return
os.mkdir(newDirName)
然后把要复制的文件夹里的文件(不包括目录),都作为任务加入进队列里
def getFilesCount(dirName):
if dirName == None or not os.path.isdir(dirName): # 合法性判断
return
files = os.listdir(dirName) # 列出所有文件名
for item in files:
absolutePath = dirName + os.sep + item # 文件绝对路径的拼接
if os.path.isdir(absolutePath):
getFilesCount(absolutePath) # 文件夹则递归
else: # 否则入队
if q.full():
print("缓存已满")
else:
q.put(absolutePath)
由于os.listdir()返回的列表里,元素都是文件的相对路径,因此要进行绝对路径的拼接,才能判断是否是文件夹
获取到所有要复制的文件的绝对路径后,就可以复制了,这一任务就交给多进程。
首先定义一个方法,做为多进程要执行的任务
def copyFile(destination, file, oldDir):
if destination == None or file == None: # 合法性判断
return
if (not os.path.isdir(destination)) or (not os.path.exists(file)): # 合法性判断
return
subDir = file[file.find(oldDir) + len(oldDir) + 1:file.rfind(os.sep)] # 字符串切片,获取新文件对应的新子文件夹相对路径
newDir = destination + os.sep + subDir # 新子文件夹绝对路径
if not os.path.exists(newDir):
os.mkdir(newDir)
fileName = file[file.rfind(os.sep) + 1:] # 新文件文件名
newFileName = newDir + os.sep + fileName # 新文件绝对路径
newFile = open(newFileName, "wb") # 文件IO
newFile.write(open(file, "rb").read())
newFile.close()
此方法比较复杂,因为python没有像java一样强大的字符串操作
以复制文件夹D:\szcPython到文件夹D:\test为例,此方法三个入参destination是D:\test,file是D:\szcPython里的一个文件的绝对路径,这一项从队列里取,比如值为D:\szcPython\dir\szc.txt,oldDir则为D:\szcPython
经过一系列合法性判断后,先通过切片,获取file的父文件夹在源文件夹里的相对路径,存在subDir里,比如值为dir
然后subDir和目的文件夹绝对路径拼接,形成新的父文件夹绝对路径,存在newDir里,比如值为D:\test\dir
然后创建文件夹,并且在通过切片的方式获取file的文件名,比如值为szc.txt
然后文件名和newDir拼接,得到新文件的绝对路径,存在newFileName里,比如值为D:\test\dir\szc.txt
之后就是文件IO,完成复制
而后,就可以构造进程池,不断从队列里取出待复制的文件绝对路径,调用任务方法去执行了
pool = multiprocessing.Pool(processes=3) # 最大容量为3的进程池
while not q.empty(): # 队列不空,一直取元素,即待复制文件的绝对路径
pool.apply_async(func=copyFile, args=(newDirName, q.get(), dirName, )) # 布置任务,args必须传参数入元组,并且最后一个参数必须写上,
pool.close() # 先close,再join。等待进程池执行结束再结束主进程
pool.join()
while not q.empty(): # 主进程阻塞在此,直到队列为空
pass
print("复制完毕")
之后,我们就可以启动程序了。再次之前,为了避免入口函数的重复执行,以及确定子进程能执行任务方法,需要改变main()方法的调用方式,并且在main()方法里,加入一句话
def main():
multiprocessing.freeze_support() # 保证多进程的执行
...
if __name__ == "__main__": # 主进程才会执行main()
main()
最后,整个文件的代码如下
import os, multiprocessing
q = None
def getFilesCount(dirName):
if dirName == None or not os.path.isdir(dirName):
return
files = os.listdir(dirName)
for item in files:
absolutePath = dirName + os.sep + item
if os.path.isdir(absolutePath):
getFilesCount(absolutePath)
else:
if q.full():
print("缓存已满")
else:
q.put(absolutePath)
def copyFile(destination, file, oldDir):
if destination == None or file == None:
return
if (not os.path.isdir(destination)) or (not os.path.exists(file)):
return
subDir = file[file.find(oldDir) + len(oldDir) + 1:file.rfind(os.sep)]
newDir = destination + os.sep + subDir
if not os.path.exists(newDir):
os.mkdir(newDir)
fileName = file[file.rfind(os.sep) + 1:]
newFileName = newDir + os.sep + fileName
newFile = open(newFileName, "wb")
newFile.write(open(file, "rb").read())
newFile.close()
def main():
multiprocessing.freeze_support()
dirName = "D:\\szcPython"
newDirName = "D:\\test"
if not os.path.isdir(dirName):
print("无此文件夹")
return
if os.path.exists(newDirName):
print("目标文件夹已存在")
return
os.mkdir(newDirName)
global q
q = multiprocessing.Manager().Queue()
getFilesCount(dirName)
pool = multiprocessing.Pool(processes=3)
while not q.empty():
pool.apply_async(func=copyFile, args=(newDirName, q.get(), dirName, ))
pool.close()
pool.join()
while not q.empty():
pass
print("复制完毕")
if __name__ == "__main__":
main()
测试结果如下
可见,文件夹的复制已经完成
虽然python可以实现多进程,但进程的创建和销毁比线程的创建和销毁要消耗的资源大得多,因此,多任务我还是优先考虑多线程。