你知道嗎?
在我的心里
你是多么的重要
就像
恩
請允許我來一段 freestyle
你們準備好了妹油
你看
這個碗
它又大又圓
就像
這條面
它又長又寬
你們
在這里
看文章
覺得 很開心
就像
我在這里
給你們
寫文章
覺得很開心
skr~~
不好意思
走錯片場了
ok..
接下來,就是
學習 python 的正確姿勢
咱們在上一次的
python爬蟲13 | 秒爬,這多線程爬取速度也太猛了,這次就是要讓你的爬蟲效率杠杠的
了解了一些 python 高效爬蟲的概念
比如多線程、多進程、協程等
那么我們這一篇就開始了解多線程的具體使用
在 python 中
常用的多線程的模塊有這么幾個
_thread
threading
Queue
之前有個 thread 模塊
被 python3 拋棄了
改名為 _thread
但其實 _thread 也沒什么人用
因為 _thread 有的 threading 都有
_thread 沒有的 threading 依然有
那么接下來我們就先來玩玩 threading 吧
在此之前
(請允許小帥b又開始吹水了~)
介紹一下 小帥b 的一點背景
小帥b呢
平常上班時間都會去河邊摸魚
每天得摸 20 條魚
一條一條的摸
為的是什么
為的是安撫這些魚的心情
這樣以后送到餐前的紅燒魚才更加美味
恩
小帥b每天得摸 20 條魚
每隔一秒鐘摸一條
也就是這樣
import time
def moyu_time(name, delay, counter):
while counter:
time.sleep(delay)
print("%s 開始摸魚 %s" % (name, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
counter -= 1
if __name__ == '__main__':
moyu_time('小帥b',1,20)
后來
小帥b知道了多線程
拍腦一想
我靠
應該把小明和小紅拉過來
讓他們一起幫我摸魚啊
也就是讓小明和小紅同時一人摸 10 條魚
想想就開心
在 小帥b 的威逼利誘下
他們倆不情愿的手拉著手來到了河邊
小帥b看小紅是女生
就讓小紅每摸一條魚休息 2 秒鐘
而小明每摸一條魚休息 1 秒鐘
先扔一段代碼給你
# encoding = utf-8
import threading
import time
# 創建一個線程子類
class MyThread(threading.Thread):
def __init__(self,threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("開始線程:" + self.name)
moyu_time(self.name, self.counter, 10)
print("退出線程:" + self.name)
def moyu_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print("%s 開始摸魚 %s" % (threadName, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
counter -= 1
# 創建新線程
# 小帥b找了兩個人來摸魚
# 讓小明摸一次魚休息1秒鐘
# 讓小紅摸一次魚休息2秒鐘
thread1 = MyThread(1, "小明", 1)
thread2 = MyThread(2, "小紅", 2)
# 開啟新線程
thread1.start()
thread2.start()
# 等待至線程中止
thread1.join()
thread2.join()
print ("退出主線程")
在這里呢
我們創建了一個線程類
然后繼承 threading.Thread
在我們這個線程類里面定義了一個 run 方法
這個 run 方法去調用了摸魚的方法
可以看到我們創建了兩個線程
一個叫小明線程
一個叫小紅線程
thread1 = MyThread(1, "小明", 1)
thread2 = MyThread(2, "小紅", 2)
當我們的線程調用 start 方法的時候
它們就會去執行 run 方法
而我們用到的 join 方法呢
是為了讓線程執行完
再終止主程序
運行一下就是這樣
開始線程:小明
開始線程:小紅
小明 開始摸魚 2019-03-10 23:15:26
小紅 開始摸魚 2019-03-10 23:15:27
小明 開始摸魚 2019-03-10 23:15:27
小明 開始摸魚 2019-03-10 23:15:28
小紅 開始摸魚 2019-03-10 23:15:29
小明 開始摸魚 2019-03-10 23:15:29
小明 開始摸魚 2019-03-10 23:15:30
小明 開始摸魚 2019-03-10 23:15:31
小紅 開始摸魚 2019-03-10 23:15:31
小明 開始摸魚 2019-03-10 23:15:32
小明 開始摸魚 2019-03-10 23:15:33
小紅 開始摸魚 2019-03-10 23:15:33
小明 開始摸魚 2019-03-10 23:15:34
小紅 開始摸魚 2019-03-10 23:15:35
小明 開始摸魚 2019-03-10 23:15:35
退出線程:小明
小紅 開始摸魚 2019-03-10 23:15:37
小紅 開始摸魚 2019-03-10 23:15:39
小紅 開始摸魚 2019-03-10 23:15:41
小紅 開始摸魚 2019-03-10 23:15:43
小紅 開始摸魚 2019-03-10 23:15:45
退出線程:小紅
退出主線程
Process finished with exit code 0
恩
小帥b再也不用摸魚了
后來小明和小紅都不樂意了
憑什么就我們兩個摸魚
這時候 小帥b 只能去找更多人了
連 小帥b 家的狗都叫過來了
然后
就瘋狂的開啟線程
thread1 = MyThread(1, "小明", 1)
thread2 = MyThread(2, "小紅", 2)
thread3 = MyThread(3, "小黃", 2)
thread4 = MyThread(4, "小綠", 2)
...
thread5 = MyThread(55, "小青", 2)
thread6 = MyThread(56, "小白", 2)
thread7 = MyThread(57, "小狗", 2)
stop?。。?br>
這可不行
因為頻繁的創建線程 銷毀線程
非常的浪費資源
所以呢
應該把他們放到池子里面去一起洗澡
哈,也就是
線程池
通過線程池就可以重復利用線程
不會造成過多的浪費
在 python 中
可以使用 ThreadPoolExecutor 來實現線程池
我們來往池子里塞 20 個線程
然后在循環的時候每次拿一個線程來摸魚
def moyu_time(name, delay, counter):
while counter:
time.sleep(delay)
print("%s 開始摸魚 %s" % (name, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
counter -= 1
if __name__ == '__main__':
pool = ThreadPoolExecutor(20)
for i in range(1,5):
pool.submit(moyu_time('xiaoshuaib'+str(i),1,3))
運行一下
xiaoshuaib1 開始摸魚 2019-03-10 23:30:10
xiaoshuaib1 開始摸魚 2019-03-10 23:30:11
xiaoshuaib1 開始摸魚 2019-03-10 23:30:12
xiaoshuaib2 開始摸魚 2019-03-10 23:30:13
xiaoshuaib2 開始摸魚 2019-03-10 23:30:14
xiaoshuaib2 開始摸魚 2019-03-10 23:30:15
xiaoshuaib3 開始摸魚 2019-03-10 23:30:16
xiaoshuaib3 開始摸魚 2019-03-10 23:30:17
xiaoshuaib3 開始摸魚 2019-03-10 23:30:18
xiaoshuaib4 開始摸魚 2019-03-10 23:30:19
xiaoshuaib4 開始摸魚 2019-03-10 23:30:20
xiaoshuaib4 開始摸魚 2019-03-10 23:30:21
可以看到
我們每次從線程池里面去拿一個線程來摸魚
這樣就不會去重復的創建銷毀線程了
當然
我們還可以用一個叫做 Queue 的隊列來創建線程池
隊列嘛~
就是可以往里塞東西
也可以往里拉東西
所以我們在使用隊列的時候
最常用的方法就是 put 和 get 了
還是拿摸魚為例
我們創建一個長度為 6 的隊列
接著根據隊列的長度創建了線程
每個線程都讓它們處于守護狀態
也就是需要的時候
馬上執行
def queue_pool():
queue = Queue(6)
for i in range(queue.maxsize):
t = CustomThread(queue)
t.setDaemon(True)
t.start()
接著我們就可以用 put 方法
把我們想做的事情往隊列里面塞
比如這里我們想要摸魚
for i in range(20):
queue.put(moyu)
queue.join()
def moyu():
print(" 開始摸魚 %s" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
要執行的話就需要去隊列里面取了
q_method = self.__queue.get()
q_method()
self.__queue.task_done()
完整代碼如下
import threading
import time
from queue import Queue
class CustomThread(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.__queue = queue
def run(self):
while True:
q_method = self.__queue.get()
q_method()
self.__queue.task_done()
def moyu():
print(" 開始摸魚 %s" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
def queue_pool():
queue = Queue(5)
for i in range(queue.maxsize):
t = CustomThread(queue)
t.setDaemon(True)
t.start()
for i in range(20):
queue.put(moyu)
queue.join()
if __name__ == '__main__':
queue_pool()
本篇就到這里吧
ps:本來想接著寫一下用多線程來爬取網站的,篇幅有限,咱們下一篇再見
peace
點個好看啊~~(破音)