寫在前面
從底層到第三方庫,全面講解python的異步編程。這節(jié)講述的是python的多線程實現(xiàn),純干貨,無概念,代碼實例講解。
本系列有6章左右,點擊頭像或者專欄查看更多內容,陸續(xù)更新,歡迎關注。
部分資料來源及參考鏈接:https://www.bilibili.com/video/BV1Li4y1j7RY/
multiprocessing(多進程)
現(xiàn)在讓我們初步進入多進程,這個就是python的多進程包,是自帶的,簡單示例:
import multiprocessing#進程包
import time
def start():
time.sleep(2)#讓程序沉睡 2 秒
print(multiprocessing.current_process().name)#打印進程名字
print(multiprocessing.current_process().pid)#打印pid
print(multiprocessing.current_process().is_alive())#打印進程是否活著
if __name__ == "__main__":
print('程序開始')
p = multiprocessing.Process(target = start)#只用寫函數(shù)名 不要加括號
p.start()#開始
p.join()#堵塞
print('程序結束')
此時,并不是一個進程打開多個線程,而是多個進程,所以每次執(zhí)行有不同的pid。
結果如下:
進程通信
本身進程是無法通信的,借助別的數(shù)據(jù)結構,就可以實現(xiàn)進程通信了,一般是棧和隊列,就像這樣:
from multiprocessing import Process,Queue
def write(q):#放入隊列
print('加入隊列成功:{}'.format(Process.pid))#打印進程pid
for i in range(10):# 0~9
print('往隊列放入:{}'.format(i))
q.put(i)#放入
def read(q):#讀取隊列
print('加入隊列成功:{}'.format(Process.pid))#打印進程pid
while True:#一有東西 就馬上讀取
value = q.get()#讀取
print('獲取隊列中的東西:{}'.format(value))
if __name__ == "__main__":
#由于Python的多進程默認無法進行通信 因為是并發(fā)執(zhí)行的
#所以要借助別的數(shù)據(jù)結構
#一般用棧 或者 隊列
q = Queue()#實例化Queue 隊列
pw = Process(target = write,args =(q,))#創(chuàng)建寫入進程
pr = Process(target = read,args = (q,))#創(chuàng)建讀取進程
pw.start()#啟動寫入
pr.start()#啟動讀取
pw.join()#堵塞讀取
python當中實現(xiàn)了棧和隊列,非常方便,如果你運行上述代碼,你會發(fā)現(xiàn)程序沒有結束,讀取進程它還在反復讀取。這其實就和golang中的管道類似。此處可以先做了解。
進程池
可以使用map方法批量提交目標
import multiprocessing
def index_pool(data):
res = data * data
return res
if __name__ == "__main__":
data = list(range(100))#100個任務
pool = multiprocessing.Pool(processes = 4)#進程池大小為4
pool_out_puts = pool.map(index_pool,data)#一次性提交大量任務
# pool_out_puts = pool.apply(index_pool,args=(10,))#一個個提交
pool.close()#關閉進程 不再創(chuàng)建進程
pool.join()#堵塞進程
print('Pool {}'.format(pool_out_puts))
運行結果:
你會發(fā)現(xiàn)執(zhí)行速度非???/p>
為什么進程池這么快呢?
這就是與多線程的區(qū)別,每個進程是獨立的,不會受到GIL鎖的控制,速度非???/p>
異步進程池
上述的例子中,進程是同步執(zhí)行的,如何寫出異步的效果呢?
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor,as_completed
import time
number_list = [1,2,3,4,5,6,7,8,9,10]
def add_number(data):#這個函數(shù) 只能消耗CPU資源 沒啥意義
item = count(data)
return item
def count(number):#單純計算 隨便寫
for i in range(0,5000000):
i = i + 1
return i * number
if __name__ == "__main__":
start_time = time.time()#程序啟動時間
with ProcessPoolExecutor(max_workers = 5) as t:# max_workers參數(shù)為 你要開多少個進程
for item in number_list:#提交任務
t.submit(add_number,item)
# reqs = [t.submit(add_number,item) for item in number_list]#提交任務 簡潔寫法
# for req in as_completed(reqs):# 轉成 可迭代對象
# print(req.result())#打印信息
print('程序總耗時:{}'.format(time.time() - start_time))
由于沒有GIL鎖的限制,執(zhí)行會非???。
進程池和異步進程池的區(qū)別是什么呢?
在Python中,進程池(Process Pool)和異步進程池(Asyncio Process Pool)是用于并行處理任務的兩種不同的機制。
進程池(Process Pool):
進程池是通過multiprocessing模塊提供的一種機制,它允許你創(chuàng)建一組預先初始化的進程,用于執(zhí)行任務。你可以將任務提交給進程池,進程池會自動分配可用的進程來執(zhí)行任務。進程池可以通過Pool類來創(chuàng)建。
進程池適用于CPU密集型任務,可以充分利用多核處理器的并行性。它通過創(chuàng)建多個進程來同時執(zhí)行任務,每個進程都有自己的Python解釋器和GIL,因此可以實現(xiàn)真正的并行執(zhí)行。進程池在處理大量計算密集型任務時通常具有較好的性能。
異步進程池(Asyncio Process Pool):
異步進程池是通過concurrent.futures和asyncio模塊提供的一種機制,它允許在異步環(huán)境中并行處理任務。異步進程池是建立在異步編程的基礎上,可以在單個線程中同時執(zhí)行多個任務。
異步進程池適用于IO密集型任務,如網(wǎng)絡請求、文件讀寫等。它利用異步編程的特性,通過在任務之間進行切換來提高效率,避免了線程切換的開銷。異步進程池在處理大量IO密集型任務時通常具有較好的性能。
進程池和線程池處理IO密集型的任務都很快嗎?
異步線程池和異步進程池的主要區(qū)別在于線程池使用的是線程,而進程池使用的是進程。
線程池:在Python中,線程是由操作系統(tǒng)管理的,多個線程共享同一進程的內存空間,因此線程之間的切換開銷較小。線程池適用于IO密集型任務,如網(wǎng)絡請求、文件讀寫等,因為在這些任務中,大部分時間都是在等待IO操作完成,線程可以在等待期間切換執(zhí)行其他任務,提高效率。
進程池:進程是由操作系統(tǒng)管理的,每個進程都有獨立的內存空間,進程之間切換的開銷較大。進程池適用于CPU密集型任務,如數(shù)據(jù)處理、圖像處理等,因為這些任務需要大量的計算資源,多個進程可以并行執(zhí)行,提高效率。
無論是線程池還是進程池,在處理IO密集型任務時都可以提高效率。但對于CPU密集型任務,由于Python的全局解釋器鎖(GIL)的存在,多線程并不能真正實現(xiàn)并行執(zhí)行,因此在這種情況下使用進程池更為合適。如果需要同時處理大量IO密集型和CPU密集型任務,可以結合使用線程池和進程池來充分利用多核資源。
為什么不直接用進程池呢?
進程會特別占用內存,能夠使用線程池的場景,還是使用線程池更好。
用實例解釋進程池和異步進程池的區(qū)別
以網(wǎng)絡爬蟲為例:
當使用進程池和異步進程池來實現(xiàn)網(wǎng)絡爬蟲項目時,它們的處理邏輯和性能表現(xiàn)有一些區(qū)別。
使用進程池的處理邏輯如下:
- 創(chuàng)建一個進程池對象,設置進程數(shù)為10。
- 將待爬取的URL列表分成若干個子任務,每個子任務包含多個URL。
- 使用進程池的map()方法,將子任務提交給進程池。
- 進程池會自動將子任務分配給空閑的進程進行處理,每個進程負責處理一個子任務。
- 當所有子任務都完成時,進程池會返回結果,可以通過返回的結果來獲取爬取的數(shù)據(jù)。
使用異步進程池的處理邏輯如下:文章來源:http://www.zghlxwxcb.cn/news/detail-770258.html
- 創(chuàng)建一個異步進程池對象,設置進程數(shù)為10。
- 將待爬取的URL列表分成若干個子任務,每個子任務包含多個URL。
- 使用異步進程池的submit()方法,將每個子任務提交給異步進程池。
- 異步進程池會立即返回一個Future對象,表示該子任務的執(zhí)行狀態(tài)。
- 可以通過Future對象的result()方法來獲取子任務的結果,如果子任務還未完成,result()方法會阻塞等待直到結果可用。
- 可以使用concurrent.futures.as_completed()函數(shù)來迭代Future對象,獲取已完成的子任務結果。
在性能方面,異步進程池通常比進程池更快。這是因為異步進程池可以同時執(zhí)行多個任務,不需要等待一個任務完成后才能執(zhí)行下一個任務,從而提高了效率。而進程池則需要按順序逐個處理任務,無法并行執(zhí)行。對于IO密集型的任務,異步進程池的性能提升更為明顯,因為它可以充分利用CPU等待IO的時間來執(zhí)行其他任務。文章來源地址http://www.zghlxwxcb.cn/news/detail-770258.html
到了這里,關于由淺入深走進Python異步編程【多進程】(含代碼實例講解 || multiprocessing、異步進程池、進程通信)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!