我有以下代码生成10个从文件列表中复制文件的线程。我一遍又一遍地调用这个不同的文件列表,我发现一旦fileQueue用完了,线程似乎不会死...我注意到代码似乎因长操作而变慢,然后我在线程中崩溃并开始在Thread-45线程中看到" Exception:"!
这是我的代码,从我在阅读手册时所知道的一切来看,这非常干净简单:
import Queue, threading
from PyQt4 import QtCore, QtGui
import shutil
fileQueue = Queue.Queue()
class ThreadedCopy:
totalFiles = 0
copyCount = 0
lock = threading.Lock()
def __init__(self, inputList, progressBar=False):
self.totalFiles = len(inputList)
print str(self.totalFiles) + " files to copy."
if progressBar:
progressBar = QtGui.QProgressDialog("Copying files...", "Cancel", 0, self.totalFiles)
progressBar.setMinimumDuration(0)
progressBar.setWindowModality(QtCore.Qt.WindowModal)
self.threadWorkerCopy(inputList, progressBar)
else:
self.threadWorkerCopy(inputList)
def CopyWorker(self, progressBar):
while True:
fileName = fileQueue.get()
shutil.copy(fileName[0], fileName[1])
fileQueue.task_done()
with self.lock:
self.copyCount += 1
if not progressBar:
print str(self.copyCount) + "of" + str(self.totalFiles)
percent = (self.copyCount * 100) / self.totalFiles
print "File copy: " + str(percent) + "%"
else:
progressBar.setValue(self.copyCount)
def threadWorkerCopy(self, fileNameList, progressBar=False):
threadCount = 10
for i in range(threadCount):
t = threading.Thread(target=self.CopyWorker, args=(progressBar,))
t.daemon = True
t.start()
for fileName in fileNameList:
fileQueue.put(fileName)
fileQueue.join()
有谁知道为什么线程不只是在这段代码的调用之间彻底死亡?根据我的理解,一旦fileQueue耗尽,那么他们应该悄然死!
编辑:这是固定代码
import Queue, threading
from PyQt4 import QtCore, QtGui
import shutil
fileQueue = Queue.Queue()
class ThreadedCopy:
totalFiles = 0
copyCount = 0
lock = threading.Lock()
def __init__(self, inputList, progressBar=False):
self.totalFiles = len(inputList)
print str(self.totalFiles) + " files to copy."
if progressBar:
progressBar = QtGui.QProgressDialog("Copying files...", "Cancel", 0, self.totalFiles)
progressBar.setMinimumDuration(0)
progressBar.setWindowModality(QtCore.Qt.WindowModal)
self.threadWorkerCopy(inputList, progressBar)
else:
self.threadWorkerCopy(inputList)
def CopyWorker(self, progressBar):
while True:
fileName = fileQueue.get()
if fileName is None:
fileQueue.task_done()
break
shutil.copy(fileName[0], fileName[1])
fileQueue.task_done()
with self.lock:
self.copyCount += 1
if not progressBar:
percent = (self.copyCount * 100) / self.totalFiles
print "File copy: " + str(percent) + "%"
else:
progressBar.setValue(self.copyCount)
def threadWorkerCopy(self, fileNameList, progressBar=False):
threads = []
threadCount = 10
for fileName in fileNameList:
fileQueue.put(fileName)
for i in range(threadCount):
t = threading.Thread(target=self.CopyWorker, args=(progressBar,))
t.daemon = True
t.start()
threads.append(t)
fileQueue.put(None)
for t in threads:
t.join()
答案 0 :(得分:1)
为什么你认为线程会死? CopyWorker
中的任何内容都没有突破while True
循环,因此我希望线程无限期地保持活跃状态。一旦消耗了所有项目,他们将被永久阻止,尝试从空队列中get
另一个值,但他们不会退出或释放他们的资源。
如果您希望线程在没有更多工作要做的情况下退出,您需要告诉他们这样做。一种常见的方法是在队列上发送一个标记值,消费线程将识别为不再有数据的信号。您需要为您已启动的每个帖子发送一份哨兵副本。这是基于您当前代码的快速未经测试的解决方案。我使用None
作为哨兵,因为它看起来不像文件名的正常值。
def CopyWorker(self, progressBar):
while True:
fileName = fileQueue.get()
if fileName is None: # check for sentinel value here
fileQueue.task_done()
return
shutil.copy(fileName[0], fileName[1])
fileQueue.task_done()
with self.lock:
self.copyCount += 1
if not progressBar:
print str(self.copyCount) + "of" + str(self.totalFiles)
percent = (self.copyCount * 100) / self.totalFiles
print "File copy: " + str(percent) + "%"
else:
progressBar.setValue(self.copyCount)
def threadWorkerCopy(self, fileNameList, progressBar=False):
threadCount = 10
for i in range(threadCount):
t = threading.Thread(target=self.CopyWorker, args=(progressBar,))
t.daemon = True
t.start()
for fileName in fileNameList:
fileQueue.put(fileName)
for i in range(threadCount): # send sentinel values from here
fileQueue.put(None)
fileQueue.join()
为简单起见,我还可以做一些其他事情。例如,保持对您开始的每个线程的引用并且join
它们都来自主线程以确保它们全部退出可能是个好主意。这可能是join
队列的替代方案。如果线程正确退出,那么线程也没有理由成为守护进程。
您还可以重新排序部分代码,这样您就不需要两个for i in range(threadCount)
循环。如果你先将所有值put
放入队列中,那么在启动线程之前,你可以将这两个循环组合起来。
答案 1 :(得分:0)
您可能忘记为每个帖子调用.join
。来自documentation
需要在fileQueue.join()
之后添加代码。但是你应该在list
之后将所有帖子添加到t.start()
(看一个例子)
for i in range(threadCount):
fileQueue.put(None)
for t in threads:
t.join()