前言
上篇文章介紹了 Python 中的多線程。今天來介紹下編程中常會用到的一個數據結構 – 隊列。
不知道大家是否還記得什麽是數據結構呢?在很早很早以前,Python小課堂的初期,講了許多 Python 原生的數據結構。比如 list、tuple、dict 等。。。
既然叫數據結構,實際上就是爲了給計算機存儲數據用的一種結構體。不同的數據結構都有其不同的特點。那今天就來簡單地聊聊隊列!
隊列的概念
抛開計算機知識體系,在咱們的生活中,隊列這個詞其實挺好想象的,因爲無時無刻都可以見到。比如等公交的時候,需要排隊。比如買東西交錢的時候,也要排隊。在這些例子中,由人們有序形成的隊形就叫隊列。
生活中的排隊,有沒有什麽特性而言呢?大家可以思考下,再往下看。
普通隊列的特性,即先進先出(FIFO,first in first out)。對應到生活中,怎麽理解這個先進先出?其實很好理解。
拿排隊買東西來說,每次排在隊首的人,交完錢肯定是當前隊列中第一個離開收銀台的人。當隊首的人離開了,那麽後面的所有人都要往前走,繼續結賬。
對應到計算機中的隊列,就是因爲第一個人先排的隊,所以他第一個交完錢就可以離開了,即先進先出。(多說句,計算機世界許多東西其實就是抽象的現實世界示例。)
心細的同學會發現,在上面將普通兩字標粗了,那一定還有一些其它的常用特殊隊列,比如優先級隊列。(PriorityQueue)
這次拿去銀行辦業務來舉例。生活中我們去銀行辦理業務,一般都需要去機器拿號,然後等待著櫃台業務人員叫號。叫到你,你就過去處理就行了。但是銀行是有 vip 服務的,擁有 vip 權益的人可以更快的享受到業務辦理,也就是說人家比你有更高的優先權。vip特權通道,你值得擁有!
了解了生活中的例子,再來看看比較專業的定義:
優先級隊列(priority queue) 是0個或多個元素的集合,每個元素都有一個優先權,對優先級隊列執行的操作有(1)查找(2)插入一個新元素 (3)刪除 一般情況下,查找操作用來搜索優先權最大的元素,刪除操作用來刪除該元素 。對于優先權相同的元素,可按先進先出次序處理或按任意優先權進行。
百度百科
也就是說,在優先級隊列中,每個人都有一個優先權對應,誰的優先權高,誰就會先被處理。大家了解即可。
示例演示
示例情景:
假設下面有 6 個美少女,她們准備去量身高,恰好這幾個妹紙是按照從高到低,從大到小排好隊的。
每走一個去量身高,這個隊列中就會少一個人。當然,隊首在左,隊尾在右,于是她們的變化是下面這樣:
。。。。。。省略,直到:
代碼演示
是不是好多人看到這裏就不打算看了。。不是讓你來看美少女的餵。。。下面才是重點~
第一部分代碼:
import queue import threading num_worker_threads = 5 def do_work(beauty_dict): print(f"妹紙名字{beauty_dict['name']},年齡{beauty_dict['age']}") def worker(): while True: item = q.get() if item is None: break do_work(item) q.task_done()
簡單的講解下,在 Python3 中,有個模塊叫 queue。裏面實現了幾個隊列的數據結構。首先看 do_work() 函數,其中它就是用來打印妹子姓名和年齡的。worker() 函數中寫了一層死循環,只要隊列中有妹紙的數據,就一直執行打印,直到隊列中的妹紙都沒了,就跳出。
第二部分代碼:
q = queue.Queue() threads = [] for i in range(num_worker_threads): t = threading.Thread(target=worker) t.start() threads.append(t) beauty_girls = [{"name": "小H", "age": 23}, {"name": "小E", "age": 22}, {"name": "小D", "age": 21}, {"name": "小C", "age": 20}, {"name": "小B", "age": 19}, {"name": "小A", "age": 18}, ] for item in beauty_girls: q.put(item) # block until all tasks are done q.join() # stop workers for i in range(num_worker_threads): q.put(None) for t in threads: t.join()
首先創建隊列,其次,讓這 6 個美少女開始依次排入到隊列中,開啓多線程去執行 worker() 這個函數。worker() 函數就是第一部分代碼中,從隊列裏一個個取出妹紙,在調用 do_work() 打印她們的姓名和年齡。
關于隊列的 join() 方法,可以看到官方給的英文注釋,大意是當所有任務完成時才繼續執行後面的代碼,否則處于阻塞狀態。
代碼中涉及的方法,老規矩,希望大家可以自己去查閱 Python 官方文檔,搜索 queue 即可看到。當然如果懶得動手的話,筆者這裏截圖幾個常用方法:
將妹紙放入隊列中:
從隊列中獲取妹紙:
獲取隊列中妹紙的個數:
總結
通過兩篇文章,簡單的介紹了一下多線程和隊列,目的是爲了接下來的多線程隊列爬蟲示例做准備,如果對這些不了解的話,在後面的代碼中是很難看懂的。
關于隊列的使用,最常用的方法應該就是放入、獲取、判斷隊列的大小。其實這三點在大部分數據結構裏都是常用的操作,熟練掌握即可。
本章完整代碼,就是文中第一部分和第二部分的代碼拼接,因爲太長,所以分開講了下。
如果大家有什麽問題想溝通,可以留言交流哈!