python Event事件、進(jìn)程池與線程池、協(xié)程解析
Event事件
用來(lái)控制線程的執(zhí)行
出現(xiàn)e.wait(),就會(huì)把這個(gè)線程設(shè)置為False,就不能執(zhí)行這個(gè)任務(wù);
只要有一個(gè)線程出現(xiàn)e.set(),就會(huì)告訴Event對(duì)象,把有e.wait的用戶全部改為True,剩余的任務(wù)就會(huì)立馬去執(zhí)行。由一些線程去控制另一些線程,中間通過(guò)Event。
from threading import Event from threading import Thread import time # 調(diào)用Event實(shí)例化出對(duì)象 e = Event() # # # 若該方法出現(xiàn)在任務(wù)中,則為False,阻塞 # e.wait() # False # # 若該方法出現(xiàn)在任務(wù)中,則將其他線程的False改為True,進(jìn)入就緒態(tài)和運(yùn)行態(tài) # e.set() # True def light(): print('紅燈亮...') time.sleep(5) # 應(yīng)該發(fā)出信號(hào),告訴其他線程準(zhǔn)備執(zhí)行 e.set() # 將car中的False變?yōu)門rue print('綠燈亮...') def car(name): print('正在等紅燈...') # 讓所有汽車任務(wù)進(jìn)入阻塞態(tài) e.wait() # False print(f'{name}正在加速飄逸...') # 讓一個(gè)light線程控制多個(gè)car線程 t = Thread(target=light) t.start() for i in range(10): t = Thread(target=car, args=(f'汽車{i}號(hào)', )) t.start()
進(jìn)程池與線程池
進(jìn)程池與線程池是用來(lái)控制當(dāng)前程序允許創(chuàng)建(進(jìn)程/線程)的數(shù)量
作用:保證在硬件允許的范圍內(nèi)創(chuàng)建(進(jìn)程/線程)的數(shù)量
線程池使用一:
from concurrent.futures import ThreadPoolExecutor import time pool = ThreadPoolExecutor(5) # 5代表只能開啟5個(gè)進(jìn)程, 不加默認(rèn)使用cpu的進(jìn)程數(shù) # ThreadPoolExecutor(5) # 5代表只能開啟5個(gè)線程 # pool.submit() #異步提交任務(wù), 括號(hào)里傳函數(shù)地址 def task(): print('線程任務(wù)開始了...') time.sleep(1) print('線程任務(wù)結(jié)束了...') for line in range(5): pool.submit(task)
使用二:
from concurrent.futures import ThreadPoolExecutor import time pool = ThreadPoolExecutor(5) # 5代表只能開啟5個(gè)進(jìn)程, 不加默認(rèn)使用cpu的進(jìn)程數(shù) # ThreadPoolExecutor(5) # 5代表只能開啟5個(gè)線程 # pool.submit() #異步提交任務(wù), 括號(hào)里傳函數(shù)地址 def task(): print('線程任務(wù)開始了...') time.sleep(1) print('線程任務(wù)結(jié)束了...') return 123 # 回調(diào)函數(shù) def call_back(res): print(type(res)) res2 = res.result() # 注意:賦值操作不要與接收的res同名 print(res2) for line in range(5): pool.submit(task).add_done_callback(call_back)
pool.shutdown() 會(huì)讓所有線程池的任務(wù)結(jié)束后,才往下執(zhí)行代碼
多線程爬取梨視頻
利用requests模塊,封裝底層socket套接字
- 主頁(yè)中獲取所有視頻id號(hào),拼接視頻詳情頁(yè)url
- 在視頻詳情頁(yè)中獲取真實(shí)視頻url srcUrl=
- 往真實(shí)視頻url地址發(fā)送請(qǐng)求獲取 視頻 二進(jìn)制數(shù)據(jù)
- 最后把視頻二進(jìn)制數(shù)據(jù)保存到本地
協(xié)程
- 進(jìn)程: 資源單位
- 線程: 執(zhí)行單位
- 協(xié)程: 在單線程下實(shí)現(xiàn)并發(fā)
注意: 協(xié)程不是操作系統(tǒng)資源,目的是讓單線程實(shí)現(xiàn)并發(fā)
協(xié)程目的
- 操作系統(tǒng):使用多道技術(shù),切換 + 保存狀態(tài),一個(gè)是遇到IO, 另一個(gè)是CPU執(zhí)行時(shí)間過(guò)長(zhǎng)
- 協(xié)程:通過(guò)手動(dòng)模擬操作系統(tǒng) “多道計(jì)數(shù)”, 實(shí)現(xiàn) 切換 + 保存狀態(tài)
- 手動(dòng)實(shí)現(xiàn),遇到IO切換,欺騙操作系統(tǒng)誤以為沒(méi)有IO操作
- 單線程時(shí),遇到IO,就切換 + 保存狀態(tài)
- 單線程時(shí),對(duì)于計(jì)算密集型,來(lái)回切換 + 保存狀態(tài)反而效率更低
優(yōu)點(diǎn):在IO密集型的情況下,會(huì)提高效率
缺點(diǎn):若在計(jì)算密集型的情況下,來(lái)回切換,反而效率更低
import time def func1(): for i in range(10000000): i+1 def func2(): for i in range(10000000): i+1 start = time.time() func1() func2() stop = time.time() print(stop - start) # 1.0312113761901855 # 基于yield實(shí)現(xiàn)并發(fā) 在計(jì)算密集型的情況下效率更低 def func1(): while True: 10000000+1 yield def func2(): g = func1() for i in range(10000000): i+1 next(g) # 每次執(zhí)行next相當(dāng)于切換到func1下面 start = time.time() func2() stop = time.time() print(stop - start) # 1.3294126987457275
gevent
gevent是一個(gè)第三方模塊,可以幫你監(jiān)聽I(yíng)O操作,并切換
使用gevent的目的:在單線程下實(shí)現(xiàn),遇到IO就會(huì) 保存狀態(tài) + 切換
import time from gevent import monkey monkey.patch_all() # 可以監(jiān)聽該程序下所有的IO操作 from gevent import spawn, joinall # 用于做切換 + 保存狀態(tài) def func1(): print('1') time.sleep(1) # IO操作 def func2(): print('2') time.sleep(3) def func3(): print('3') time.sleep(5) start = time.time() s1 = spawn(func1) s2 = spawn(func2) s3 = spawn(func3) s1.join() # 發(fā)送信號(hào),相當(dāng)于等待自己(在單線程的情況下) s2.join() s3.join() # joinall((s1, s2, s3)) # 一個(gè)個(gè)執(zhí)行很麻煩,可以用joinall把這些全部裝進(jìn)去 end = time.time() print(end - start) # 5.006161451339722
TCP服務(wù)端socket套接字實(shí)現(xiàn)協(xié)程
服務(wù)端:
from gevent import monkey from gevent import spawn import socket monkey.patch_all() server = socket.socket() server.bind(('127.0.0.1', 9999)) server.listen(5) def task(conn): while True: try: data = conn.recv(1024) if len(data) == 0: break print(data.decode('utf-8')) send_data = data.upper() conn.send(send_data) except Exception: break conn.close() def server2(): while True: conn, addr = server.accept() print(addr) spawn(task, conn) if __name__ == '__main__': s = spawn(server2) s.join()
客戶端:
import socket from threading import Thread, current_thread def client(): client = socket.socket() client.connect(('127.0.0.1', 9999)) number = 0 while True: send_data = f'{current_thread().name} {number}' client.send(send_data.encode('utf-8')) data = client.recv(1024) print(data.decode('utf-8')) number += 1 for i in range(400): t = Thread(target=client) t.start()
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
解決tensorflow打印tensor有省略號(hào)的問(wèn)題
今天小編就為大家分享一篇解決tensorflow打印tensor有省略號(hào)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-02-02教你用python編寫腳本實(shí)現(xiàn)自動(dòng)簽到
這篇文章主要介紹了教你怎樣用python編寫腳本實(shí)現(xiàn)自動(dòng)簽到,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08Python之freegames?零代碼的22個(gè)小游戲集合
這篇文章主要介紹了,Python之freegames?零代碼的22個(gè)小游戲集合,文章內(nèi)容詳細(xì),簡(jiǎn)單易懂,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2023-01-01Django事務(wù)transaction的使用以及多個(gè)裝飾器問(wèn)題
這篇文章主要介紹了Django事務(wù)transaction的使用以及多個(gè)裝飾器問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08詳解python tkinter包獲取本地絕對(duì)路徑(以獲取圖片并展示)
這篇文章主要給大家介紹了關(guān)于python tkinter包獲取本地絕對(duì)路徑(以獲取圖片并展示)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09詳解如何利用Python實(shí)現(xiàn)報(bào)表自動(dòng)化
這篇文章主要介紹了報(bào)表自動(dòng)化的流程,并教你用Python實(shí)現(xiàn)工作中的一個(gè)報(bào)表自動(dòng)化實(shí)戰(zhàn),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-03-03Python實(shí)現(xiàn)過(guò)迷宮小游戲示例詳解
這篇文章主要介紹的是基于Python實(shí)現(xiàn)一個(gè)簡(jiǎn)單的過(guò)迷宮小游戲,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Python有一定的幫助,感興趣的可以學(xué)習(xí)一下2021-12-12