队列中的所有任务都已完成,但程序未继续

时间:2015-04-17 15:16:21

标签: python python-multithreading

我有一个这样定义的线程类:

#!/usr/bin/python

import threading
import subprocess

class PingThread (threading.Thread):
    ipstatus = ''
    def __init__(self, ip):
        threading.Thread.__init__(self)
        self.ipaddress = ip


    def ping(self, ip):
        print 'Pinging ' + ip + '...'
        ping_response = subprocess.Popen(["ping", "-c", "1", ip], stdout=subprocess.PIPE).stdout.read()
        if '100.0% packet loss' not in str(ping_response):
            return True
        return False

    def set_ip_status(self, status):
        self.ipstatus = status

    def get_ip_status(self):
        return self.ipstatus

    def run(self):
        self.ipaddress = self.ipaddress.strip('\n\t')
        pingResponse = self.ping(self.ipaddress)
        if pingResponse:
            self.set_ip_status(self.ipaddress + ' is up!')
        else:
            self.set_ip_status(self.ipaddress + ' is down!')

我正在浏览一个ip地址列表并将其发送到PingThread并让此类ping ip地址。当这些线程全部完成后,我希望它通过调用get_ip_status()来获取每个线程的状态。我的代码中有q.join(),应该等到队列中的所有项都完成(根据我的理解,如果我错了,请更正我,还是线程新手)但我的代码永远不会通过{ {1}}。我测试了并且所有线程都完成了,并且所有IP地址都被ping了,但是q.join没有意识到这一点。为什么是这样?我究竟做错了什么?我正在创建这样的线程:

q.join()

2 个答案:

答案 0 :(得分:3)

你误解了Queue.join的工作原理。 Queue.join旨在与Queue.task_done一起使用;在制作人端,您put项目的一端是Queue,然后拨打Queue.join等待处理您put的所有项目。然后在消费者端,get Queue中的Queue.task_done项,处理它,然后在完成后调用task_done。在put QueueQueue.join的所有项目调用Queue后,join将取消阻止。

但你不是那样做的。您只需启动一堆线程,将它们添加到task_done,然后在其上调用Queue.get。您根本没有使用Queue.join,而您只是在Queue之后调用Thread,看起来您只是在用它们来获取线程对象之后完成。但这并不是真的如何运作; Queue.join不知道其中有Thread个对象,只需调用join就不会等待其中的threads = [] for ip in trainips: thread = PingThread(ip) thread.start() threads.append(thread) for thread in threads: thread.join() print thread.get_ip_status() 个对象完成。

实际上,看起来您需要做的就是将线程放在列表中,然后在每个线程上调用{{1}}。

{{1}}

答案 1 :(得分:2)

正如文档所说,Queue.join

  

阻止,直到队列中的所有项目都被获取并处理完毕。

但是你不是每次都试图获取这些项目,直到之后 join(即便如此,你也不会标记它们被处理过)。

所以,在你完成join循环之前,你无法超越while,在你超越join之前你无法进入,所以你永远阻止

要使join工作,您必须将最后三行改为:

while not q.empty():
    print q.get().get_ip_status()
    q.task_done()
q.join()

然而,一个更简单的解决方案就是不要join队列。相反,你可以 join所有线程;然后你知道get所有值都是安全的。但请注意,如果你这样做,队列就没有理由成为Queue;它可以只是一个普通的list。此时你已经有效地获得了dano's answer

或者,您可以更改代码以实际使用队列。而不是将线程放入队列中,将队列传递给线程函数,并将其结果放在队列中,而不是将其存储为属性。然后,你可以在你正在做的时候循环get(),它会自动处理你需要的所有阻塞。文档中Queue.join的示例显示了如何完成您想要做的事情。

后一种解决方案的优点是你不再需要你的任务和线程一对一地映射 - 例如,使用一个包含16个线程的池来运行128个任务,你仍然会得到128个队列上的值。 *


*但是如果你想这样做,你可能会使用multiprocessing.dummy.Pool或(来自PyPI上的concurrent.futures后端)futures.ThreadPoolExecutor而不是自己构建它。 /子>