python线程不会死

时间:2017-09-20 05:49:23

标签: python multithreading python-multithreading

我有以下代码生成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()

2 个答案:

答案 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()