一、什么是多任務
??如果一個操作系統上同時運行了多個程序,那么稱這個操作系統就是 多任務的操作系統,例如: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, timefrom multiprocessing import Processdef task():val = 1for i in range(1, 100000):val *= iif __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, timefrom threading import Threaddef task():val = 1for i in range(1, 100000):val *= iif __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 timefrom multiprocessing import Processdef 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 timefrom threading import Threaddef 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}")