python线程不会消亡

2024-06-24 11:45:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我有以下代码,它产生10个线程,从文件列表复制文件。我为不同的文件列表一遍又一遍地调用这个函数,我发现一旦fileQueue用完,线程似乎不会死掉。。。我注意到代码在长时间的操作中似乎变慢了,然后我在线程中崩溃了,开始看到“线程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()

有人知道为什么线程不只是在调用这段代码之间干净地死去吗?据我所知,一旦文件队列用完了,它们就应该静静地死去!在

编辑:这是固定代码

^{pr2}$

Tags: 文件代码importselfqueuedeffilename线程
2条回答

你觉得这些线为什么会死?在CopyWorker中没有任何东西可以突破while True循环,因此我希望线程可以无限期地保持活动状态。一旦所有的项目都被消耗了,它们将被永久阻止尝试从空队列中get另一个值,但是它们不会退出或释放它们的资源。在

如果你想让你的线程在没有更多的工作要做时退出,你需要告诉他们这样做。一种常见的方法是通过队列发送一个sentinel值,消费线程会将其识别为不再有数据的信号。您需要为每个启动的线程发送一个sentinel副本。下面是一个基于当前代码的快速未经测试的解决方案。我使用None作为sentinel,因为它看起来不像是文件名的正常值。在

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首先将所有值放入队列中,那么在启动线程之前,可以将这两个循环组合起来。在

您可能忘记为每个线程调用.join。来自documentation

需要在fileQueue.join()后添加代码。但是您应该将所有线程添加到list之后的t.start()(请看一个示例)

for i in range(threadCount):
    fileQueue.put(None)
for t in threads:
    t.join()

相关问题 更多 >