python Event事件、進程池與線程池、協(xié)程解析
Event事件
用來控制線程的執(zhí)行
出現(xiàn)e.wait(),就會把這個線程設(shè)置為False,就不能執(zhí)行這個任務(wù);
只要有一個線程出現(xiàn)e.set(),就會告訴Event對象,把有e.wait的用戶全部改為True,剩余的任務(wù)就會立馬去執(zhí)行。由一些線程去控制另一些線程,中間通過Event。
from threading import Event
from threading import Thread
import time
# 調(diào)用Event實例化出對象
e = Event()
#
# # 若該方法出現(xiàn)在任務(wù)中,則為False,阻塞
# e.wait() # False
# # 若該方法出現(xiàn)在任務(wù)中,則將其他線程的False改為True,進入就緒態(tài)和運行態(tài)
# e.set() # True
def light():
print('紅燈亮...')
time.sleep(5)
# 應(yīng)該發(fā)出信號,告訴其他線程準(zhǔn)備執(zhí)行
e.set() # 將car中的False變?yōu)門rue
print('綠燈亮...')
def car(name):
print('正在等紅燈...')
# 讓所有汽車任務(wù)進入阻塞態(tài)
e.wait() # False
print(f'{name}正在加速飄逸...')
# 讓一個light線程控制多個car線程
t = Thread(target=light)
t.start()
for i in range(10):
t = Thread(target=car, args=(f'汽車{i}號', ))
t.start()
進程池與線程池
進程池與線程池是用來控制當(dāng)前程序允許創(chuàng)建(進程/線程)的數(shù)量
作用:保證在硬件允許的范圍內(nèi)創(chuàng)建(進程/線程)的數(shù)量
線程池使用一:
from concurrent.futures import ThreadPoolExecutor
import time
pool = ThreadPoolExecutor(5) # 5代表只能開啟5個進程, 不加默認(rèn)使用cpu的進程數(shù)
# ThreadPoolExecutor(5) # 5代表只能開啟5個線程
# pool.submit() #異步提交任務(wù), 括號里傳函數(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個進程, 不加默認(rèn)使用cpu的進程數(shù)
# ThreadPoolExecutor(5) # 5代表只能開啟5個線程
# pool.submit() #異步提交任務(wù), 括號里傳函數(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() 會讓所有線程池的任務(wù)結(jié)束后,才往下執(zhí)行代碼
多線程爬取梨視頻
利用requests模塊,封裝底層socket套接字
- 主頁中獲取所有視頻id號,拼接視頻詳情頁url
- 在視頻詳情頁中獲取真實視頻url srcUrl=
- 往真實視頻url地址發(fā)送請求獲取 視頻 二進制數(shù)據(jù)
- 最后把視頻二進制數(shù)據(jù)保存到本地
協(xié)程
- 進程: 資源單位
- 線程: 執(zhí)行單位
- 協(xié)程: 在單線程下實現(xiàn)并發(fā)
注意: 協(xié)程不是操作系統(tǒng)資源,目的是讓單線程實現(xiàn)并發(fā)
協(xié)程目的
- 操作系統(tǒng):使用多道技術(shù),切換 + 保存狀態(tài),一個是遇到IO, 另一個是CPU執(zhí)行時間過長
- 協(xié)程:通過手動模擬操作系統(tǒng) “多道計數(shù)”, 實現(xiàn) 切換 + 保存狀態(tài)
- 手動實現(xiàn),遇到IO切換,欺騙操作系統(tǒng)誤以為沒有IO操作
- 單線程時,遇到IO,就切換 + 保存狀態(tài)
- 單線程時,對于計算密集型,來回切換 + 保存狀態(tài)反而效率更低
優(yōu)點:在IO密集型的情況下,會提高效率
缺點:若在計算密集型的情況下,來回切換,反而效率更低
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實現(xiàn)并發(fā) 在計算密集型的情況下效率更低 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是一個第三方模塊,可以幫你監(jiān)聽IO操作,并切換
使用gevent的目的:在單線程下實現(xiàn),遇到IO就會 保存狀態(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ā)送信號,相當(dāng)于等待自己(在單線程的情況下)
s2.join()
s3.join()
# joinall((s1, s2, s3)) # 一個個執(zhí)行很麻煩,可以用joinall把這些全部裝進去
end = time.time()
print(end - start) # 5.006161451339722
TCP服務(wù)端socket套接字實現(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()
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Django事務(wù)transaction的使用以及多個裝飾器問題
這篇文章主要介紹了Django事務(wù)transaction的使用以及多個裝飾器問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
詳解python tkinter包獲取本地絕對路徑(以獲取圖片并展示)
這篇文章主要給大家介紹了關(guān)于python tkinter包獲取本地絕對路徑(以獲取圖片并展示)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

