爬蟲筆記4 程序多線程threading與Queue結合使用,Queue用法詳細解讀

1.Queue的用法

通常配合threading使用,創建一個隊列,多個線程可以從隊列中提取任務,返回輸入任務
那麼具體是怎麼配送threading模塊使用的呢?
舉個例子,比如你要下載一個文件,可是你發現對方給你限制了你的下載速度,每個文件只准10kb的下載,這時候你可以將下載文件所有的請求丟到一個隊列裏面 Queue.put()(假設1000個請求),這個隊列就是Queue,然後你設置100個線程,每個線程都可以直接通過Queue.get()來拿到屬於自己的任務,最後使用Queue.task_done()告訴隊列,這個任務我完成了。這樣你就相當於擁有了下載100個文件的速度來下載這一個文件。

from queue import Queue
q = Queue()#可以設置maxsize設置最多隊列長度
方法 作用
Queue.qsize() 返回隊列的大小
Queue.empty() 如果隊列爲空,返回True,反之False
Queue.full() 與empty相反,返回True,反之False
Queue.get() 獲取隊列 ,get put都有timeout參數,表示等待時間
Queue.get_nowait() 非阻塞獲取隊列
Queue.put() 寫入隊列
Queue.put_nowait() 非阻塞寫入隊列
Queue.task_done() 告訴隊列該任務已經處理完畢
Queue.join() 等到隊列爲空,再執行別的操作

具體使用

先看看這個代碼

from queue import Queue
import threading
import time

start_time = time.time()
q = Queue()

for i in range(1,11): #爲q隊列添加10個數字
    q.put(i)

def f1(): #假設是一個需要1s時間才能完成的程序
    while True: #循環,使線程一直從q中獲取數據
        num = q.get()
        print("現在處理隊列中的", num)
        time.sleep(1)
        print("隊列{}完成".format(str(num)))
        q.task_done()

def f2(): #計時器
    for i in range(10):
        time.sleep(1)
        print("程序已運行{}s".format(str(int(time.time()-start_time))))

thread_list = []

t1 = threading.Thread(target=f1)
thread_list.append(t1)
t2 = threading.Thread(target=f2)
thread_list.append(t2)

for t in thread_list:
    t.setDaemon(True) #子線程會在不重要的主線程結束,子線程結束
    t.start()

q.join() #讓主線程阻塞,等待隊列任務全部完成,防止主線程結束導致子線程也被殺死

print("主線程結束")

結果

現在處理隊列中的 1
隊列1完成
現在處理隊列中的 2
程序已運行1s
隊列2完成
現在處理隊列中的 3
程序已運行2s
隊列3完成
現在處理隊列中的 4
程序已運行3s
隊列4完成
現在處理隊列中的 5
程序已運行4s
隊列5完成
現在處理隊列中的 6
程序已運行5s
隊列6完成
現在處理隊列中的 7
程序已運行6s
程序已運行7s
隊列7完成
現在處理隊列中的 8
隊列8完成
程序已運行8s
現在處理隊列中的 9
程序已運行9s
隊列9完成
現在處理隊列中的 10
隊列10完成
程序已運行10s
主線程結束

進程完成,退出碼 0

可能有人就要說,你這用不用Queue結果不都一樣嗎,還不是10s完成
說了要用多線程的,不要急,這個只是個對照代碼,只需要修改一點點,就可以完成多線程

3個線程去完成任務

from queue import Queue
import threading
import time

start_time = time.time()
q = Queue()

for i in range(1,11): #爲q隊列添加10個數字
    q.put(i)

def f1(): #假設是一個需要1s時間才能完成的程序
    while True:
        num = q.get()
        print("現在處理隊列中的", num)
        time.sleep(1)
        print("隊列{}完成".format(str(num)))
        q.task_done()

def f2(): #計時器
    for i in range(10):
        time.sleep(1)
        print("程序已運行{}s".format(str(int(time.time()-start_time))))

thread_list = []
for i in range(3): #3個線程
    t1 = threading.Thread(target=f1)
    thread_list.append(t1)
t2 = threading.Thread(target=f2)
thread_list.append(t2)

for t in thread_list:
    t.setDaemon(True) #子線程會在不重要的主線程結束,子線程結束
    t.start()

q.join() #讓主線程阻塞,等待隊列任務全部完成,防止主線程結束導致子線程也被殺死

print("主線程結束")

結果

現在處理隊列中的 1
現在處理隊列中的 2
現在處理隊列中的 3
隊列3完成
程序已運行1s
隊列2完成
現在處理隊列中的 4
隊列1完成
現在處理隊列中的 5
現在處理隊列中的 6
隊列4完成
隊列5完成
程序已運行2s
隊列6完成
現在處理隊列中的 7
現在處理隊列中的 8
現在處理隊列中的 9
程序已運行3s
隊列7完成
現在處理隊列中的 10
隊列9完成
隊列8完成
程序已運行4s
隊列10完成
主線程結束

沒錯只用了4s就完成了10個數據的處理

如果用5個線程,那就是2s

現在處理隊列中的 1
現在處理隊列中的 2
現在處理隊列中的 3
現在處理隊列中的 4
現在處理隊列中的 5
隊列1完成
隊列4完成
現在處理隊列中的 6
隊列3完成
隊列2完成
現在處理隊列中的 7
現在處理隊列中的 8
現在處理隊列中的 9
程序已運行1s
隊列5完成
現在處理隊列中的 10
隊列6完成
隊列7完成
程序已運行2s
隊列8完成
隊列9完成
隊列10完成
主線程結束

那有人就要說,那我直接使用10個線程來處理這10個數據不就行了嗎,幹嘛還用着Queue呢
很簡單的道理,如果這個數據是動態的,可能前一秒給10個後一秒給4個,再加上程序沒辦法及時將數據處理完,那麼這個程序很容易出錯或者卡住。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章