Python多线程文件去重

注:本文基于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值列表,以及重复图片列表,因此我们定义三个线程锁,当然也可以只定义一个锁,但是锁的粒度过大的话就很影响线程的执行效率,因此我们细化锁的粒度,这样线程间对全局资源的操作更为灵活。

加锁的操作在对列表读取、添加前,操作完成后,立马释放该锁,以便其他线程可以持有该锁。

你可能感兴趣的:(Python)