1. 進程基礎概念
1.1 什么是進程?
進程(Process)是指正在執行的程序,是程序執行過程中的一次指令、數據集等的集合。簡單來說,進程就是程序的一次執行過程,它是一個動態的概念。
想象你打開電腦上的音樂播放器聽歌,同時又在用瀏覽器上網,這兩個就是不同的進程。操作系統會為每個運行的程序創建一個進程,讓它們看起來像是同時在運行。
1.2 進程的特征
-
動態性:進程有創建、執行、暫停、終止等生命周期
-
并發性:多個進程可以同時存在于內存中,在一段時間內交替執行
-
獨立性:每個進程擁有獨立的地址空間和系統資源
-
異步性:進程執行速度不可預知,可能隨時被中斷
1.3 進程與程序的區別
區別點 | 程序 | 進程 |
---|---|---|
狀態 | 靜態的代碼集合 | 動態的執行過程 |
生命周期 | 永久保存 | 暫時存在 |
資源占用 | 不占用系統資源 | 占用CPU、內存等資源 |
2. 進程調度算法
操作系統使用調度算法決定哪個進程可以使用CPU資源:
2.1 先來先服務(FCFS)
-
按照進程到達的順序執行
-
簡單但不利于短作業
-
示例:排隊買票,先到先得
processes = ["P1", "P2", "P3"]
for p in processes:print(f"正在執行{p}")
2.2 短作業優先(SJF)
-
優先執行預計運行時間短的進程
-
能減少平均等待時間
-
但難以準確預估作業長度
processes = [("P1",3), ("P2",1), ("P3",2)]
processes.sort(key=lambda x: x[1]) # 按執行時間排序
2.3 時間片輪轉(RR)
-
每個進程分配一個時間片(如100ms)
-
時間片用完就切換到下一個進程
-
公平但上下文切換開銷大
from collections import deque
ready_queue = deque(["P1", "P2", "P3"])
time_slice = 1 # 單位時間while ready_queue:p = ready_queue.popleft()print(f"執行{p} {time_slice}單位時間")ready_queue.append(p) # 重新加入隊列
2.4 多級反饋隊列
-
結合了多種算法的優點
-
設置多個優先級不同的隊列
-
新進程進入高優先級隊列
-
長時間運行的進程會被移到低優先級隊列
3. 進程的并行與并發
3.1 基本概念
并行(Parallelism):
指多個任務真正同時執行,需要多核CPU支持。就像餐廳有多個廚師同時做不同的菜。
# 并行示例(假設4核CPU)
from multiprocessing import Pooldef task(n):return n * nif __name__ == '__main__':with Pool(4) as p: # 創建4個進程print(p.map(task, [1, 2, 3, 4])) # 4個任務真正同時執行
并發(Concurrency):
指多個任務交替執行,在單核CPU上通過快速切換實現"看似同時"。就像一個廚師輪流做多道菜。
# 并發示例
from multiprocessing import Process
import timedef task(name):print(f"{name}開始")time.sleep(1)print(f"{name}結束")if __name__ == '__main__':processes = []for i in range(3): # 單核CPU上交替執行p = Process(target=task, args=(f"任務{i}",))p.start()processes.append(p)for p in processes:p.join()
3.2 關鍵區別
特性 | 并行 | 并發 |
---|---|---|
硬件要求 | 需要多核CPU | 單核即可 |
執行方式 | 真正同時執行 | 交替執行 |
效率 | 更高(理想情況) | 相對較低 |
適用場景 | CPU密集型任務 | I/O密集型任務 |
圖示 | 🟢🟢🟢(同時進行) | 🟢→🟡→🔴(快速切換) |
3.3 Python中的實現特點
-
GIL限制:由于全局解釋器鎖(GIL),Python多線程無法實現真正的并行,多進程是Python實現并行的主要方式
-
進程開銷:進程創建和上下文切換開銷比線程大,適合CPU密集型任務
-
multiprocessing模塊:繞過GIL限制,充分利用多核CPU
4. 同步/異步與阻塞/非阻塞
4.1 進程的三種基本狀態
4.2 同步 vs 異步
同步(Synchronous):
-
像排隊買奶茶,必須等前一個人完成才能輪到你
-
代碼示例:
from multiprocessing import Process, Lockdef sync_task(lock, num):with lock: # 同步鎖print(f"進程{num}開始工作")time.sleep(1)print(f"進程{num}結束")if __name__ == '__main__':lock = Lock()for i in range(3):Process(target=sync_task, args=(lock, i)).start()
異步(Asynchronous):
-
像取號等餐,拿到號后可以去做其他事
-
代碼示例:
from multiprocessing import Pooldef async_task(num):print(f"開始異步任務{num}")time.sleep(1)return num * 10if __name__ == '__main__':with Pool(3) as p:results = [p.apply_async(async_task, (i,)) for i in range(3)]for res in results:print(res.get()) # 需要時才獲取結果
4.3 阻塞 vs 非阻塞
阻塞(Blocking):
-
像打電話訂餐,必須等客服回應才能做下一件事
-
典型表現:
join()
,?get()
等方法會阻塞
p = Process(target=time.sleep, args=(5,))
p.start()
p.join() # 這里主程序會阻塞等待
print("子進程結束")
非阻塞(Non-blocking):
-
像發短信訂餐,發完就可以做其他事
-
典型表現:不調用
join()
或使用Queue
的nowait
processes = []
for i in range(3):p = Process(target=time.sleep, args=(i,))p.start()processes.append(p)# 主進程繼續執行其他代碼...
print("主進程繼續運行")# 最后再統一等待
for p in processes:p.join()
4.4 四種組合模式(重點理解)
-
同步阻塞:
-
最傳統的方式
-
示例:直接函數調用,等待返回結果
result = time.sleep(3) # 同步調用,阻塞等待
-
-
同步非阻塞:
-
輪詢檢查狀態
-
示例:檢查進程是否完成
while True:if not p.is_alive():breaktime.sleep(0.1)
-
-
異步阻塞:
-
較少使用
-
示例:使用回調但主線程等待
def callback(result):print("回調結果:", result)with Pool() as pool:res = pool.apply_async(func, args, callback=callback)res.wait() # 這里又變成了阻塞
-
-
異步非阻塞:
-
最高效的方式
-
示例:使用進程池+回調
def callback(result):print("Got result:", result)with Pool() as pool:for i in range(5):pool.apply_async(func=time.sleep,args=(1,),callback=callback)print("主進程繼續執行...")pool.close()pool.join()
-
4.5 實際應用場景建議
模式 | 適用場景 | Python實現方式 |
---|---|---|
同步阻塞 | 簡單線性任務 | 直接函數調用 |
同步非阻塞 | 需要輪詢的任務 | 循環檢查is_alive() |
異步阻塞 | 較少使用 | apply_async +wait() |
異步非阻塞 | 高并發I/O操作 | 進程池+回調函數 |
4.6??完整代碼示例(綜合應用)
"""
多進程模式下載器示例
演示并行、異步非阻塞模式
"""
from multiprocessing import Pool
import time
import randomdef download(url):print(f"開始下載 {url}")time.sleep(random.uniform(1, 3)) # 模擬下載時間print(f"完成下載 {url}")return f"{url}_內容"def save_content(result):print(f"保存結果: {result}")if __name__ == '__main__':urls = ["http://example.com/1","http://example.com/2","http://example.com/3","http://example.com/4"]with Pool(4) as pool: # 創建進程池# 異步非阻塞模式提交任務results = [pool.apply_async(download, (url,), callback=save_content) for url in urls]print("主進程可以繼續處理其他任務...")# 最終等待所有任務完成pool.close()pool.join()print("所有下載任務完成!")
這個示例展示了:
-
并行執行(4個下載任務同時進行)
-
異步非阻塞模式(提交任務后立即繼續執行)
-
回調機制(下載完成后自動保存)
5. Python中的進程操作
Python通過multiprocessing
模塊實現多進程編程,下面介紹幾種創建進程的方式。
5.1 方式一:使用Process類直接創建
from multiprocessing import Process
import osdef func(num):print(f"這是一個普通方法{num}")print(f"我是子進程,我的pid:{os.getpid()},我的父進程編號:{os.getppid()}")if __name__ == '__main__':# 創建進程對象p1 = Process(name="路飛", target=func, args=(1,))# 啟動進程p1.start()# 輸出父進程信息print(f"我是父進程,我的pid:{os.getpid()},我的父進程編號:{os.getppid()}")print(p1)
代碼解析:
-
導入
Process
類和os
模塊 -
定義目標函數
func
,它將在子進程中執行 -
創建
Process
實例,指定目標函數和參數 -
調用
start()
方法啟動進程 -
os.getpid()
獲取當前進程ID,os.getppid()
獲取父進程ID
5.2 方式二:繼承Process類創建
from multiprocessing import Process
import osclass MyProcess(Process):def __init__(self, *args):super(MyProcess, self).__init__()self.args = argsdef run(self):print(f"我是子進程{self.args[0]}")if __name__ == '__main__':p1 = MyProcess(1)p2 = MyProcess(2)p3 = MyProcess(3)p1.start()p2.start()p3.start()
代碼解析:
-
自定義類繼承
Process
類 -
重寫
run()
方法,定義進程執行邏輯 -
創建自定義類的實例并啟動
-
這種方式更面向對象,適合復雜任務
5.3 進程常用方法
from multiprocessing import Process
import timedef fun():print("我是子進程")for i in range(3):time.sleep(5)print(f"我是子進程{i}")if __name__ == '__main__':p1 = Process(name='路飛', target=fun)p1.start()p1.join() # 父進程等待子進程結束for i in range(2):time.sleep(1)print(f"我是父進程{i}")
關鍵方法:
-
p.start()
:啟動進程,并調用該子進程中的p.run() - ?p.run (): 進程啟動時運行的方法,正是它去調用target 指定的函數,我們自定義類的類中一定要實現該方法
-
p.join([timeout]):主線程等待p終止(強調:是主線程處于等的狀態,而p是處于運行的狀態)。
timeout 是可選的超時時間,需要強調的是, p.join 只能 join 住 start 開啟的進程,而不能 join 住 run 開啟的進程 -
p.is_alive():如果p仍然運行,返回True
-
p.terminate():強制終止進程p,不會進行任何清理操作,如果p創建了子進程,該子進程就成了僵尸進 程。使用該方法需要特別小心這種情況。如果p還保存了一個鎖那么也將不會被釋放,進而導致死鎖
5.4 進程常用屬性
from multiprocessing import Process
import timedef fun():for i in range(10):time.sleep(1)print("我是子進程")if __name__ == '__main__':p = Process(target=fun)p.daemon = True # 設置為守護進程p.start()time.sleep(5)print("我是父進程")
重要屬性:
-
daemon
:默認值為False,如果設為True,代表p為后臺運行的守護進程,當p的父進程終止時, p也隨之終止,并且設定為True后,p不能創建自己的新進程,必須在p.start()之前設置守護進程:跟隨著父進程的代碼執行結束,守護進程就結束 -
name
:進程名稱 -
pid
:進程ID -
exitcode
:進程在運行時為None、如果為–N,表示被信號N結束(了解即可)
6. 進程同步與通信
6.1 進程間數據隔離
from multiprocessing import Processdef work():global nn = 0print('子進程內: ', n)if __name__ == '__main__':n = 100p = Process(target=work)p.start()print('主進程內: ', n)
輸出結果:
主進程內: 100
子進程內: 0
解釋:進程間內存空間獨立,修改子進程中的變量不會影響父進程。
7. 實際應用建議
-
CPU密集型任務:適合使用多進程,可以充分利用多核CPU
-
I/O密集型任務:多線程可能更合適,避免進程創建開銷
-
守護進程:用于執行后臺任務,如日志記錄、監控等
-
進程池:當需要創建大量進程時,考慮使用
Pool
類
8. 注意事項
-
Windows平臺必須使用
if __name__ == '__main__':
保護主代碼 -
進程創建和銷毀開銷較大,不宜創建過多進程
-
進程間通信需要使用隊列(Queue)或管道(Pipe)等機制
-
避免僵尸進程(子進程結束但父進程未回收資源)