目錄python
早期程序員使用穿孔卡片編程,linux
只有一個計算機機房,一次只能讀取一個卡片,程序員
因此形成CPU的利用率極低編程
後來出現了聯機批處理系統,支持多用戶去使用一個計算機機房併發
再後來出現了脫機批處理系統,有了高速磁盤,提升了文件的讀取速度,優勢是提升了CPU的利用率異步
基於單核狀況下研究分爲單道和多道,單道即多個程序在使用CPU時是串行運行函數
當代計算機使用的技術 是多道技術性能
一個CPU能夠提供多個用戶去使用優化
切換 + 保存狀態操作系統
若CPU 遇到 IO 操做,會當即將當前執行程序CPU使用權斷開
優勢:CPU利用率高
若一個程序使用CPU的時間過長,會當即將當前執行程序CPU使用權斷開
缺點:程序執行效率低
並行:指的是看起來像同時在運行,實際上是多個程序不停 切換 + 保存狀態
併發:真正意義上的同時運行,在多核(多個CPU)狀況下,同時執行多個程序
執行中的程序叫作進程(Process),是一個動態的概念
在linux中查看進程信息:top
61546 root 20 0 15028 1196 836 R 4.4 0.1 0:00.35 top
程序:一堆代碼文件
進程:一堆代碼文件運行的過程
當代操做系統調度:
時間片輪轉法 + 分級反饋隊列
一、 先來先服務調度
a,b 程序,若a程序先來,先佔用CPU
缺點:程序a先使用,程序b必須等待程序a使用CPU完畢以後才能使用
二、 短做業優先調度
a,b程序,誰的用時短,優先調度誰使用CPU
缺點:
若程序a使用時間最長,有N個程序使用時間段,
必須等待全部用時短的程序結束後才能使用
三、 時間片輪轉法
CPU執行的時間1秒鐘,若加載N個程序,將時間等分紅多n個時間片供程序執行
四、 分級反饋隊列
將執行優先級分多層級別
1級:優先級最高
2級:優先級第二,以此類推
。。。
全部程序建立時都會進入就緒態,準備調度
調度後的進程,進入運行態
凡是遇到IO操做的進程,都會進入阻塞態
若IO結束,必須從新進入就緒態
指的是提交任務的方式
同步:如有兩個任務須要提交,在提交第一個任務時,必須等待該任務執行結束後,才能繼續提交併執行第二個任務。
同步演示:
# coding=utf-8 import time def test(): # 睡一秒 也是IO操做 time.sleep(1) # 計算操做不屬於IO操做 n = 0 for i in range(1000): n += i print(n) if __name__ == '__main__': test() # 先執行完test函數,再執行下面的代碼,同步操做 print("hello world")
異步:
如有兩個任務須要提交,在提交第一個任務時,不須要原地等待,當即能夠提交併執行第二個任務。
阻塞:
阻塞態,遇到IO 必定會阻塞
非阻塞:
就緒態
運行態
''' 建立進程方式一: ''' from multiprocessing import Process import time def task(name): print(f"{name} 子任務start...") time.sleep(2) print(f"{name} 子任務end....") if __name__ == '__main__': p = Process(target=task,args=("qinyj",)) p.start() print("主進程...")
代碼解釋:
p = Process(target=task,args=("qinyj",)):實例化一個對象p,Process類參數:target=函數名,args=函數參數(必須是元組 + ,)
p.start():向操做系統發送指令開啓一個子進程,至於這個子進程何時啓動,要看機器的硬件性能
打印結果:
主進程... qinyj 子任務start... qinyj 子任務end....
''' 建立進程方式二: ''' from multiprocessing import Process import time class MyProcess(Process): def run(self): print(f"子任務 start...") time.sleep(2) print(f"子任務 end....") if __name__ == '__main__': p = MyProcess() p.start() print("主進程...")
代碼解釋
p = MyProcess():實例化本身寫的一個類,這個類必須繼承Process
p.start():向操做系統發送指令開啓一個子進程,至於這個子進程何時啓動,要看機器的硬件性能
打印結果:
主進程... 子任務 start... 子任務 end....
那麼爲何咱們要用if name == 'main':?,
是由於Windows在執行這個文件的時候,會自動import導入這個文件,導入這個文件至關於又從新執行了一遍裏面的代碼,這樣子進程就從新又被建立了出來,子進程而後又運行到建立子進程的代碼。。。無限循環,最後報錯。那麼在Linux中執行這個文件不會自動import導入,它所建立的子類代碼僅僅是執行的函數代碼,fork了一塊新的內存空間執行這個函數,因此不會報錯
那麼咱們爲了統一使用,最好加上if name == 'main':,意思是做爲執行文件執行的時候,判斷它的名字,條件符合了再執行下面的代碼,這樣不管哪一個平臺運行代碼都不會報錯。
# coding=utf-8 from multiprocessing import Process import time def task(name): print(f"{name} 子任務 start...") time.sleep(2) print(f"{name} 子任務 end...") if __name__ == '__main__': p1 = Process(target=task,args=("qinyj",)) p2 = Process(target=task,args=("jack",)) p3 = Process(target=task,args=("qyj",)) p1.start() p2.start() p3.start() p1.join() p2.join() p3.join() print("主進程...")
p.join:用來告訴操做系統,讓子進程結束後,父進程在結束
# coding=utf-8 from multiprocessing import Process x = 100 def func(): print("函數執行") global x x = 200 print(f"函數內部的x:{x}") if __name__ == '__main__': p = Process(target=func) p.start() p.join() print(f"主進程的x:{x}") 函數執行 函數內部的x:200 主進程的x:100
# coding=utf-8 from multiprocessing import Process from multiprocessing import current_process import os import time def task(name): print(f"{name} 子任務start...",f"子進程進程號:{current_process().pid}") print(f"{name} 子任務start...",f"子進程進程號:{os.getpid()}",f"父進程進程號{os.getppid()}") time.sleep(200) print(f"{name} 子任務end.....",current_process().pid) if __name__ == '__main__': p = Process(target=task,args=("qinyj",)) p.start() time.sleep(1) print(p.is_alive()) p.terminate() time.sleep(1) print(p.is_alive()) p.join() print("主程序start..",os.getpid()) print("主主進程start...",os.getppid()) qinyj 子任務start... 子進程進程號:9132 qinyj 子任務start... 子進程進程號:9132 父進程進程號7348 True False 主程序start.. 7348 主主進程start... 2812
代碼解釋:
p.terminate():直接告訴操做系統,終止子進程
print(p.is_alive()):打印子進程的存活狀態(True or False)
current_process().pid:獲取當前的pid號
os.getpid():獲取當前的pid號
os.getppid():獲取當前父進程的pid號
cmd中查看進程號:tasklist |findstr 進程號
C:\Users\Administrator>tasklist | findstr 6724 # 查看子進程pid號即python解釋器的進程 python.exe 6724 Console 1 30,576 K C:\Users\Administrator>tasklist | findstr 2812 # 查看主進程pid號即pycharm解釋器的進程 pycharm64.exe 2812 Console 1 909,528 K
由進程產生的pid號會自動 由主進程回收,若是子進程存在,主進程死掉了,那麼子進程就是殭屍進程
兩種進程號回收的方法條件:
一、 join:主進程能夠等待子進程結束 pid一塊兒被回收
二、 主進程正常結束,守護進程開啓,子進程與主進程 pid被回收
# coding=utf-8 from multiprocessing import Process from multiprocessing import current_process import os import time def task(name): print(f"{name} 子進程start...",current_process().pid) time.sleep(5) print(f"{name} 子進程end...",current_process().pid) if __name__ == '__main__': p = Process(target=task,args=("qinyj",)) p.daemon = True p.start() time.sleep(1) # 停留1s 讓子進程啓動 print("主進程啓動...") qinyj 子進程start... 5680 主進程啓動...
代碼解釋:
p.daemon = True:添加守護進程,True
守護進程表示的是 主進程死的時候,不管子進程在幹什麼,直接幹掉子進程,跟着主程序一塊兒死掉
殭屍進程指的是子進程已經結束,但pid號還存在沒有被銷燬,能夠比喻人死了,身份證尚未註銷,那這個pid就會一直被佔用,那麼操做系統的進程號是有限的,若是產生大量的殭屍進程,將由於沒有可用的進程號而致使系統不能產生新的進程,此爲殭屍進程的危害,應當避免
那麼咱們能夠經過查找這個pid號kill 手動殺掉回收
殭屍進程的缺點:佔用pid號,佔用操做系統的資源
孤兒進程指的是子進程還在執行,但父進程意外結束,可是有操做系統優化機制,會提供一個孤兒院,專門幫那些已經死了的主進程 回收那些沒有父親的子進程,在linux操做系統中,這個孤兒院就是init進程,pid號是1
1 root 20 0 19364 644 424 S 0.0 0.1 0:17.45 init