需要关于 Python 线程/队列的帮助

7
import threading
import Queue
import urllib2
import time

class ThreadURL(threading.Thread):

    def __init__(self, queue):
        threading.Thread.__init__(self)

        self.queue = queue

    def run(self):
        while True:
            host = self.queue.get()
            sock = urllib2.urlopen(host)
            data = sock.read()

            self.queue.task_done()

hosts = ['http://www.google.com', 'http://www.yahoo.com', 'http://www.facebook.com', 'http://stackoverflow.com']
start = time.time()

def main():
    queue = Queue.Queue()

    for i in range(len(hosts)):
        t = ThreadURL(queue)
        t.start()

    for host in hosts:
        queue.put(host)

    queue.join()

if __name__ == '__main__':
    main()
    print 'Elapsed time: {0}'.format(time.time() - start)

我一直在努力理解如何执行线程,经过几个教程后,我想出了上面的方法。

它应该做的是:

  1. 初始化队列
  2. 创建我的线程池,然后排队列出主机列表
  3. 一旦队列中有主机并读取网站数据,我的ThreadURL类就会开始工作
  4. 程序应该完成

首先,我想知道的是,我是否做得正确?这是处理线程的最佳方式吗?

其次,我的程序无法退出。它会打印出“已用时间”行,然后挂起。我必须杀死终端才能让它消失。我认为这是由于我不正确地使用了“queue.join()”引起的?

3个回答

8

你的代码看起来很好,也很干净。

你的应用程序仍然“挂起”的原因是工作线程仍在运行,等待主应用程序将某些东西放入队列中,即使您的主线程已经完成。

最简单的解决方法是将线程标记为守护进程,在调用start之前执行t.daemon = True。这样,线程将不会阻止程序停止。


2

在线程运行函数中,while True循环如果发生异常,则可能不会调用task_done(),而get()已经被调用。因此,queue.join()可能永远无法结束。


2

看起来还不错。Yann提出的守护进程建议是正确的,可以解决你的卡顿问题。我唯一的问题是为什么要使用队列?因为你没有进行任何跨线程通信,所以似乎可以将主机信息作为ThreadURL init()的参数发送,并且不需要使用队列。

这样做没有问题,只是想知道为什么。


我使用队列是因为大多数我阅读的示例和教程都在使用它。为什么队列只在线程之间发送数据时有用呢?关于线程和队列的唯一信息我能找到的是教程(只展示代码,没有太多详细信息)和官方文档,但对于我这个刚入门的线程初学者来说,官方文档过于复杂,难以理解。 - dave
1
如果您只是针对每个主机启动一个线程,那么可以放弃使用队列并将单个主机传递给__init__()。但是请考虑这样一种情况:您有一个大型主机列表,想要提供给有限数量的线程。您需要使用队列来分配工作。 - Corey Goldberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接