注:本文基于python2.7.5
之前已经使用python写过文件去重的脚本(Windows下使用python删除重复图片),但是文件多了脚本运行比较慢,因此改进一下,使用多线程感受一下效果。
思路还是不变,通过比较文件的MD5值确定是否是同一文件,相似图片暂不考虑,有机会研究一下。
代码如下:
#-*- coding: UTF-8 -*-
import threading
import os
import sys
import hashlib
import time
import shutil
def thread_run(name):
threadLockForPrint.acquire()
print("starting: Thread" + str(name)) #如果不使用锁,打印可能出现串行的情况,串口也是全局资源
threadLockForPrint.release()
threadLockForPhoto.acquire()
while len(photoes) > 0:
photo = photoes.pop()
threadLockForPhoto.release()
photomd5 = getmd5(photo)
threadLockForMD5.acquire()
if photomd5 not in photoMd5List:
photoMd5List.append(photomd5)
threadLockForMD5.release()
else:
threadLockForMD5.release()
threadLockForIdentical.acquire()
identicalPhotoes.append(photo)
threadLockForIdentical.release()
threadLockForPhoto.acquire()
threadLockForPhoto.release()
def getmd5(file):
if not os.path.isfile(file):
return
f = open(file,'rb')
md5 = hashlib.md5()
md5.update(f.read())
f.close()
return md5.hexdigest()
def getFiles(targetDir):
allfiles = []
for path,dir,filelist in os.walk(targetDir):
for filename in filelist:
allfiles.append(os.path.join(path, filename))
return allfiles
if __name__ == "__main__":
dirname = sys.argv[1]
if len(sys.argv) == 3:
identicalDir = sys.argv[2]
else:
identicalDir = ""
threadLockForPhoto = threading.Lock()
threadLockForMD5 = threading.Lock()
threadLockForIdentical = threading.Lock()
threadLockForPrint = threading.Lock()
global identicalPhotoes
identicalPhotoes = []
global photoMd5List
photoMd5List = []
global photoes
photoes = getFiles(dirname)
threads = []
start = time.time()
for i in range(5): #create 5 threads
t = threading.Thread(target=thread_run, args=(i+1,))
t.start()
threads.append(t)
for thread in threads:
thread.join()
end = time.time()
last = end - start
print("There are " + str(len(identicalPhotoes)) + " identical photo(es)")
if os.path.exists(identicalDir):
for i in identicalPhotoes:
shutil.move(i, identicalDir)
print(i.decode('gbk'))
print("main thread exit: lasts for " + str(last) + "s")
脚本接收两个入参,一个是需要去重的文件目录(必须),另一个是存放重复文件的目录(可选)。如果没有指定存放重复文件的目录,则不移动重复文件,仅打印重复文件数量。
主要是使用threading这个模块实现,通过继承threading.Thread,并重写__init__和run函数,就能将自己需要运行的操作定义在线程中了。这里我们定义两个线程,然后调用线程的start()函数,之后就会调用我们定义的run函数,执行我们希望的操作。为防止主线程在子线程前结束,调用join()函数,等待所有子线程结束后,主线程再退出。
因为有多个线程在运行,我们不得不考虑同步的问题,不然数据就会出现不一致了。考虑到我们有三个全局变量,所有照片的列表,非重复照片的MD5值列表,以及重复图片列表,因此我们定义三个线程锁,当然也可以只定义一个锁,但是锁的粒度过大的话就很影响线程的执行效率,因此我们细化锁的粒度,这样线程间对全局资源的操作更为灵活。
加锁的操作在对列表读取、添加前,操作完成后,立马释放该锁,以便其他线程可以持有该锁。