一、什麼是多工
如果一個作業系統上同時執行了多個程式,那麼稱這個作業系統就是 多工的作業系統,例如:Windows、Mac、Android、IOS、Harmony 等。如果是一個程式,它可以同時執行多個事情,那麼就稱為 多工的程式。
一個 CPU 預設可以執行一個程式,如果想要多個程式一起執行,理論上就需要多個 CPU 來執行。
如果一個 CPU 是一個核心,理論上只能同時執行一個任務,但是事實上卻可以執行很多個任務。這是因為作業系統控制著 CPU,讓 CPU 做了一個特殊的事情,一會執行一個任務,然後快速的執行另一個任務,依次類推,實現了多個任務,看上去 “同時” 執行多個任務。
併發:是一個對假的多工的描述;
並行:是真的多工的描述;
二、程序與執行緒
計算機程式只是儲存在磁碟上的可執行二進位制(或其它型別)檔案。只有把它們載入到記憶體中從被作業系統呼叫,才擁有其生命期。
程序(process)則是一個執行中的程式。每個程序都擁有自己的地址空間、記憶體、資料棧以及其它用於跟蹤執行的輔助資料。作業系統管理其上所有程序的執行,併為這些程序合理分配時間。程序也可以透過派生新的程序來執行其它任務,不過因為每個新程序也都擁有自己的記憶體和資料棧等,所以只能採用程序間通訊的方式共享資料;
執行緒(thread)與程序類似,不過它們是同一個程序下執行的,並共享相同的下上文。執行緒包括開始、執行順序和結束三部分。它有一個指令指標,用於記錄當前執行的上下文。當其它執行緒執行時,它可以被搶佔(中斷)和臨時掛起(也稱為睡眠)—— 這種做法叫做讓步(yielding)。
一個程序中的各個執行緒與主執行緒共享同一片資料空間。執行緒一般是以併發方式執行的。在單核 CPU 系統中,因為真正的併發是不可能的,所以執行緒的執行實際上是這樣規劃的:每個執行緒執行一小會,然後讓步給其它執行緒(再次排隊等待更多的 CPU 時間)。在整個程序的執行過程中,每個執行緒執行它自己特定的任務,在必要時和其它執行緒進行結果通訊。
但是這種共享資料也是存在風險的。如果兩個或多個執行緒訪問同一片資料,由於資料訪問順序不同,可能導致結果不一致。這種情況通常稱為 “競態條件”(race condition)。另一個需要注意的問題時,執行緒無法給予公平的執行時間。這是因為一些函式會在完成前保持阻塞狀態,如果沒有專門為多執行緒情況進行修改,會導致 CPU 的時間分配向這些貪婪的函式傾斜。
在實現多工時,執行緒切換從系統層面遠不止儲存和恢復 CPU 上下文這麼簡單。作業系統為了程式執行的高效性,每個執行緒都有自己快取 Cache 等資料。作業系統還會幫你做這些資料的恢復操作。所以執行緒的切換比較耗效能。但是協程的切換隻是單純的操作 CPU 的上下文。
執行緒是計算機中可以被 CPU 排程的最小單元,程序是計算機資源分配的最小單元;程序作為資源分配的單位,系統在執行時會為每個程序分配不同的記憶體區域;
一個程式,至少有一個程序,一個程序中至少有一個執行緒,最終是執行緒在工作;
一個程序內可以開設多個執行緒,在用一個程序內開設多個執行緒無需再次申請空間及複製程式碼的操作,開設執行緒的開銷遠遠的要小於程序的開銷;
單核 CPU,其實是一種假的多執行緒,因為在一個時間單元內,也只能執行一個執行緒的任務。但是因為 CPU 時間單元特別短,因此感覺不出來;
三、多程序的使用場景
多程序 適合 計算密集型 的場景。
【1】、多程序的使用
import os, time
from multiprocessing import Process
def task():
val = 1
for i in range(1, 100000):
val *= i
if __name__ == "__main__":
l = []
count = int(os.cpu_count())
print(f"當前計算機CPU核心個數:{count}")
start_time = time.time()
for i in range(count):
p = Process(target=task)
p.start()
l.append(p)
for p in l:
p.join()
print(f"執行時間:{time.time() - start_time}")
【2】、多執行緒的使用
import os, time
from threading import Thread
def task():
val = 1
for i in range(1, 100000):
val *= i
if __name__ == "__main__":
l = []
count = int(os.cpu_count())
print(f"當前計算機CPU核心個數:{count}")
start_time = time.time()
for i in range(count):
t = Thread(target=task)
t.start()
l.append(t)
for t in l:
t.join()
print(f"執行時間:{time.time() - start_time}")
四、多執行緒的使用場景
多執行緒 適合 IO 密集型 場景
【1】、多程序的使用
import time
from multiprocessing import Process
def task():
time.sleep(3)
if __name__ == "__main__":
l = []
start_time = time.time()
for i in range(1000):
p = Process(target=task)
p.start()
l.append(p)
for p in l:
p.join()
print(f"執行時間:{time.time() - start_time}")
【2】、多執行緒的使用
import time
from threading import Thread
def task():
time.sleep(3)
if __name__ == "__main__":
l = []
start_time = time.time()
for i in range(1000):
t = Thread(target=task)
t.start()
l.append(t)
for t in l:
t.join()
print(f"執行時間:{time.time() - start_time}")