python 个线程没有死
python threads not dying
我有以下代码生成 10 个从文件列表复制文件的线程。我为不同的文件列表一遍又一遍地调用它,我发现一旦 fileQueue 运行 出来,线程似乎并没有死......我注意到代码似乎随着时间的推移而变慢操作,然后我在线程内崩溃并开始看到 "Exception in thread Thread-45:"!
这是我的代码,根据我在阅读手册时所知道的一切,这是非常干净和简单的:
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()
您可能忘记为每个线程调用 .join
。来自 documentation
需要在fileQueue.join()
之后添加代码。但是你应该在 t.start()
之后将所有线程添加到 list
(看一个例子)
for i in range(threadCount):
fileQueue.put(None)
for t in threads:
t.join()
为什么你认为线程会死掉? 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
ing 队列。如果线程正常退出,线程也没有理由成为守护进程。
您还可以重新排序一些代码,这样就不需要两个 for i in range(threadCount)
循环。如果您 put
首先将所有值放入队列,然后再启动线程,您可以将两个循环组合起来。
我有以下代码生成 10 个从文件列表复制文件的线程。我为不同的文件列表一遍又一遍地调用它,我发现一旦 fileQueue 运行 出来,线程似乎并没有死......我注意到代码似乎随着时间的推移而变慢操作,然后我在线程内崩溃并开始看到 "Exception in thread Thread-45:"!
这是我的代码,根据我在阅读手册时所知道的一切,这是非常干净和简单的:
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()
您可能忘记为每个线程调用 .join
。来自 documentation
需要在fileQueue.join()
之后添加代码。但是你应该在 t.start()
之后将所有线程添加到 list
(看一个例子)
for i in range(threadCount):
fileQueue.put(None)
for t in threads:
t.join()
为什么你认为线程会死掉? 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
ing 队列。如果线程正常退出,线程也没有理由成为守护进程。
您还可以重新排序一些代码,这样就不需要两个 for i in range(threadCount)
循环。如果您 put
首先将所有值放入队列,然后再启动线程,您可以将两个循环组合起来。