2012-11-20 98 views
25

我想在python中使用线程来下载大量的网页,并通过下面的代码使用队列中的一个网站。使用队列的Python中的线程

它放置了一个无限的while循环。每个线程是否连续运行直到所有线程都完成?我错过了什么。

#!/usr/bin/env python 
import Queue 
import threading 
import urllib2 
import time 

hosts = ["http://yahoo.com", "http://google.com", "http://amazon.com", 
"http://ibm.com", "http://apple.com"] 

queue = Queue.Queue() 

class ThreadUrl(threading.Thread): 
    """Threaded Url Grab""" 
    def __init__(self, queue): 
    threading.Thread.__init__(self) 
    self.queue = queue 

    def run(self): 
    while True: 
     #grabs host from queue 
     host = self.queue.get() 

     #grabs urls of hosts and prints first 1024 bytes of page 
     url = urllib2.urlopen(host) 
     print url.read(1024) 

     #signals to queue job is done 
     self.queue.task_done() 

start = time.time() 
def main(): 

    #spawn a pool of threads, and pass them queue instance 
    for i in range(5): 
    t = ThreadUrl(queue) 
    t.setDaemon(True) 
    t.start() 

    #populate queue with data 
    for host in hosts: 
    queue.put(host) 

    #wait on the queue until everything has been processed  
    queue.join() 

main() 
print "Elapsed Time: %s" % (time.time() - start) 
+0

请检查您的缩进。我试图纠正它,但'queue.join'的方式错位,它也可能在顶层。您将主机添加到队列的循环也在您创建线程的循环中,因此您将每个主机添加五次。 – mata

+0

随着缩进校正,脚本也适用于我。 – lukedays

+2

此代码看起来像是通过[here](http://www.ibm.com/developerworks/aix/library/au-threadingpython/) – daviewales

回答

16

将线程设置为daemon线程导致它们在主完成时退出。但是,是的,你是正确的,因为只要queue有其他东西会阻塞,你的线程就会持续运行。

的文档解释这个细节Queue docs

蛇皮线程文档解释的daemon一部分。

当没有活动的非后台线程时,整个Python程序将退出。

因此,当队列被清空并且queue.join在解释器退出时恢复时线程将会死亡。

编辑:缺省行为我不认为Queue修正为Queue

+1

中的一些修改复制的。get的默认行为是* block *如果队列为空,则不会引发“Empty”异常。 –

+0

如果我们在线程中睡了100毫秒,或者如果在队列中找不到任何项目,则此类时间间隔不应该改正行为。我计划跨越50个线程,以下载超过5000页。我不认为我可以拥有全部50个线程来争夺cpu资源。 – raju

+0

不,我的意思是他们会一直处于无限循环。您不必在睡眠中添加循环来释放CPU时间。 'queue.get'和http请求期间会有一个块。该块将用作上下文切换将发生在下一个线程的点。 50个线程不应该成为您的目标,因为您最终将受到下载速度以及您将执行的任何磁盘I/O的限制。 – sean

-2

是必要的,这种情况下。只有Thread使用:

import threading, urllib2, time 

hosts = ["http://yahoo.com", "http://google.com", "http://amazon.com", 
"http://ibm.com", "http://apple.com"] 

class ThreadUrl(threading.Thread): 
    """Threaded Url Grab""" 
    def __init__(self, host): 
     threading.Thread.__init__(self) 
     self.host = host 

    def run(self): 
     #grabs urls of hosts and prints first 1024 bytes of page 
     url = urllib2.urlopen(self.host) 
     print url.read(1024) 

start = time.time() 
def main(): 
    #spawn a pool of threads 
    for i in range(len(hosts)): 
     t = ThreadUrl(hosts[i]) 
     t.start() 

main() 
print "Elapsed Time: %s" % (time.time() - start) 
+3

如果将来的版本想要加载10,000个URL,这不是一个很好的计划。 –

+0

是的,我不会创建10000个线程。相反,只会创建多个包含多个网址提取的线索。 – lukedays

8

你的脚本工作正常,我,所以我想你问是怎么回事,这样你就可以更好地理解它。是的,你的子类将每个线程置于无限循环中,等待某些东西放入队列中。当发现什么东西时,它抓住它并做它的事情。然后,关键部分通知队列它已经用queue.task_done完成,并且继续等待队列中的另一个项目。

虽然所有这些都与工作线程一起进行,但主线程正在等待(加入),直到队列中的所有任务都完成为止,这将在线程发送queue.task_done标志时使用相同数量的次数作为队列中的消息。此时主线程完成并退出。由于这些是deamon线程,他们也关闭了。

这是很酷的东西,线程和队列。这是Python非常好的部分之一。你会听到关于Python中的线程是如何与GIL等搞砸的各种东西。但是如果你知道在哪里使用它们(就像本例中的网络I/O一样),它们会真的为你加快速度。一般规则是,如果你是I/O绑定,请尝试和测试线程;如果你是CPU绑定,线程可能不是一个好主意,也许尝试进程。

好运,

迈克