正确关闭守护程序线程

时间:2017-11-16 19:27:59

标签: python multithreading

我创建了自己的线程类,将在线程池中使用,如下所示:

class SendFilesThread(threading.Thread):
    execute = True

    def __init__(self, port, rate, fileQueue):
        self.daemon = True
        self.fileQueue = fileQueue
        #Other initialization

    def run(self):
        while SendFilesThread.execute == True or not self.fileQueue.empty():
            self.filename = self.fileQueue.get()
            #some file processing
            self.fileQueue.task_done()

    @staticmethod
    def terminate():
        SendFilesThread.execute = False

在程序的主线程中,在处理完所有文件后,我尝试按如下方式关闭线程池:

SendFilesThread.terminate()
for t in threadPool:
    if t.is_alive():
        t.join()
exit()

我的理解是,如果我调用join(),它将阻止调用线程(在本例中为主线程)继续,直到连接的线程完成处理。我的问题是,尽管线程完成,但它永远不会返回主线程,程序就会挂起。关闭我的线程池时我做错了吗?

2 个答案:

答案 0 :(得分:2)

有两件事。首先,从设计的角度来看,让execute标志位于类级别有点奇怪。将它作为每个类实例上的标志通常会更好。

其次,你有竞争条件。 execute标志由多个线程访问,并且不受同步原语(如互斥锁)的保护。这意味着{/ 1}}调用可以在任何工作线程检查标志之后运行(并设置标志),但之前该线程尝试将下一个队列出列文件名。因为您在没有超时的情况下调用terminate(),所以工作线程将挂起,主线程将在get()调用中阻塞。随之而来的是死锁。

有很多方法可以解决这个问题。您可以使用线程同步原语(如互斥锁)来保护t.join()标志,或使用execute对象代替简单的布尔值。

另一个,在我看来更简单,解决方案是发送一个" sentinel"同一队列上的值,表示该线程应该退出。看起来您正在发送字符串文件名,因此空字符串可能是一个不错的选择。 (threading.Event也常用。)

现在每个线程的工作循环如下所示:

None

主线程不是def run(self): while True: self.filename = self.fileQueue.get() if self.filename == '': return # Process file 静态方法,而是为每个工作线程放置一个空字符串(即terminate())。

答案 1 :(得分:1)

如果fileQueuemultiprocessing.Queuequeue.Queue,则其.get()方法将阻止,直到某些内容到达队列。尝试使用.get_nowait(),但如果要在队列为空时继续执行,则可能必须将其包装在try块中。

try:
    self.filename = self.fileQueue.get_nowait()
except queue.Empty:
    time.sleep(1)

请参阅queue docs了解更多