2012-11-20 107 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綁定,線程可能不是一個好主意,也許嘗試進程。

好運,

邁克