names data[sizefilenameif len(names work_queue put((sizenames)work_queue join(results_queue join(we now iterate over the dataand for each (sizefilename -tuple that has list of two or more potentially duplicate fileswe add the size and the filenames with paths as an item of work to the work queue since the queue is class from the queue module we don' have to worry about locking finally we join the work queue and results queue to block until they are empty this ensures that the program runs until all the work is done and all the results have been outputand then terminates cleanly def print_results(results_queue)while truetryresults results_queue get(if resultsprint(resultsfinallyresults_queue task_done(this function is passed as an argument to threading thread(and is called when the thread it is given to is started it has an infinite loop because it is to be used as daemon thread all it does is get results ( multiline string)and if the string is nonemptyit prints it for as long as results are available the beginning of the worker class is similar to what we had beforeclass worker(threading thread)md _lock threading lock(def __init__(selfwork_queuemd _from_filenameresults_queuenumber)super(__init__(self work_queue work_queue self md _from_filename md _from_filename self results_queue results_queue self number number def run(self)while truetrysizenames self work_queue get(self process(sizenames