標籤:自己的 效能 進程鎖 判斷 async 技術 狀態 服務 模式
多進程,進程queue,pipe管道,進程鎖,進程池,協程,5種網路模式(阻塞io,非阻塞io,訊號驅動io,io多工,非同步io)
多進程
import multiprocessing
每個進程都會由他的父進程進行啟動
windows中是pycharm
linux中是 multiprocessing
獲得進程id
import os
#擷取子進程id
print(os.getppid())
#擷取父進程id
print(os.getpid())
輸出
進程queue 資料不是共用
同一個進程中的線程 記憶體是共用的 用之前的線程隊列傳輸資料
但是進程之間記憶體不是共用的,進程之間的資料通訊就用到了
進程queue,它和線程queue不一樣,它是相當於複製了一份在其他進程中。
from multiprocessing import Process, Queue
def f(qq):
#在子進程中給 進程queue添加資料
qq.put([42, None, ‘hello‘])
if __name__ == ‘__main__‘:
#在父進程中建立一個進程queue隊列
q = Queue()
#建立一個進程對象,進程queue作為參數放入進去
p = Process(target=f, args=(q,))
#啟動一個進程
p.start()
print(q.get())
p.join()
輸出如下:
pipe管道 資料不是共用,只是複製一份
兩個進程之間的通訊 還可以用到管道 pipe 類似於scoket一個發一個收
manager進程資料共用
from multiprocessing import Process, Manager
def f(d, l):
‘‘‘此方法用於被進程執行‘‘‘
d[1] = ‘1‘
d[‘2‘] = "wwwww"
l=["A","B"]
print(l,d)
if __name__ == ‘__main__‘:
#等於 manager= Manager()
with Manager() as manager:
d = manager.dict() #建立一個字典用於進程之間的資料共用
l = manager.list()#建立一個列表用於進程之間的資料共用
p_list = [] #為了迴圈join的列表
for i in range(10):#迴圈10次建立10個進程
p = Process(target=f, args=(d, l))#建立進程 把字典和列表傳進去
p.start()
p_list.append(p)
for res in p_list:
res.join()
print(d)
print(l)
進程鎖
進程鎖存在的目的是為了 多個進程列印資料時不會亂
保證每個進程同一時間單獨佔一塊螢幕
進程池
進程池的作用 多進程時機器開銷很大 進程池保證在這段代碼在同一時間
運行進程的個數,避免系統開銷過大而掛掉
線程是沒有線程池的,因為它的開銷很小。需要可以用訊號量自己寫一個
協程
協程被稱為微線程,是一種使用者級的輕量級線程,擁有自己的寄存器上下文和棧
可以對任務不停的上下切換。並獨立儲存上次離開的位置,避免串列 阻塞 造成的時間浪費,從而大幅度提高效率。
效果類似於多線程,卻又不同
1.在切換時
不管進程或者線程 每次阻塞 切換都需要系統調用,先讓cpu跑作業系統的調用程式,然後再由發送器判斷跑那個 線程或進程
但協程是程式自身控制的,就沒有了線程切換的開銷。和多線程比,線程的數量越多,協程的優勢就越明顯。
2.線程鎖(互斥鎖)
協程因為只有一個線程,所以也不存在同時寫變數衝突,在協程中不需要加鎖,只判斷狀態就好,說以效率比多線程高很多。
利於cpu多核時 可以多進程+協程 充分利用了多核,又發揮了協程的高效率。
手動協程 greenlet
協程 超級自動版 gevent 最常用
封裝了手動協程(greenlet),在遇到io操作時,會自動切換到下一個方法,當下一個方法遇到io操作時,又切換回去。這就避免了因為等待io執行而造成的時間浪費。
如 線程正常串列遇到io就等待 總共需要3秒,而協程只需要2秒
如果協程方法需要參數,參照如下 f是方法,後邊是參數
最簡單的爬取網頁 相當於下載
利用了協程
協程實現socekt
用戶端
服務端
事件驅動型
現在的ui編程都是事件驅動型的
點擊一個任務,比如開啟一個文本。會把這個事件放入一個隊列中去,
然後有一個線程去處理隊列中的事件。這樣就避免了 滑鼠或者鍵盤引起的阻塞。
linux舉例
記憶體空間分為
核心空間 作業系統核心啟動並執行空間
獨立於普通的應用程式 可以訪問底層硬體 受保護的記憶體空間
使用者空間
運行程式啟動並執行空間
核心空間的記憶體 和 使用者空間的記憶體不是共用的
在寫scoket,發送資料時,發現程式不是立刻發送,而是等待緩衝的閾值滿了之後才發送。這是因為 核心空間 需要把資料拷貝到 使用者空間,這些資料拷貝操作所帶來的 CPU 以及記憶體開銷是非常大的,所以才有了緩衝。資料攢多了,一起發送。
一次IO訪問(以read舉例),資料會先被拷貝到作業系統核心的緩衝區中,然後才會從作業系統核心的緩衝區拷貝到應用程式的地址空間。所以說,當一個read操作發生時,它會經曆兩個階段:
1. 等待資料準備 (Waiting for the data to be ready)
2. 將資料從核心拷貝到進程中 (Copying the data from the kernel to the process)
因為以上兩種方式,所以產生了以下5種網路模式
阻塞io 單線程下,只能維護一個scoket連結 等待資料和拷貝資料兩個階段都屬於阻塞狀態
非阻塞io 單線程下,當使用者進程發出reav操作的時候,如果核心的資料沒有準備好,不會阻塞,而是返回error。
訊號驅動io
IO多工
特點:在單線程成下,可以迴圈維護多個scoket串連,任意一個資料準備就緒,就會返回給使用者 拷貝資料於阻塞狀態
io多工三種模式:
select()幾乎在所有的平台都支援,預設只能維護1024個scoke t。
另外有如果有1000個串連,只有最後一個串連返回資料。那麼會迴圈9999次,很浪費效能。
poll()屬於過度階段
epoll()不支援windows 支援linux
nigix 說是非同步io 其實是io多工 epoll模式
有點 如果有1000個串連,有一個串連有資料,核心會告訴哪個串連有資料,使用者直接擷取即可。
非同步io 不會對使用者進程產生阻塞,核心會等待資料準備完成,然後將資料拷貝到使用者記憶體,當這一切都完成之後,核心才會給使用者發送訊號,告訴read操作完成了。沒有任何阻塞。
非同步io用的很少 有個專門的模組asyncio
python學習筆記——拾