在作業系統中,每一個獨立運作的程式,都占有 作業系統 配置設定的資源,這些程式中間互不幹涉,都隻負責運作自己的程式代碼,這就是程序。
但是當作業系統頻繁的建立銷毀程序時,大量的系統資源被浪費在建立和銷毀的過程中。而随着多核心 cpu 的出現,線程也逐漸代替了程序,成為了作業系統 可以獨立運作的基本機關。
當程序不是多線程程式時,存在于程序當中的唯一線程,便是程序本身運作的代碼塊。
而當多線程出現在一個程序中時,則多個線程之間共享此程序的資源,并接受作業系統的排程來運作每個線程。
狀态
協程
為了了解協程的概念,我們先來了解一下并發和并行。
并行
并行比較好了解,即有多個程式在同時執行,這裡的程式指的是作業系統的線程。
每個 cpu 核心,隻能在同一個時刻運作一組指令,意味着同一時刻,一個核心上隻有一個線程在執行。
當 cpu 有四個核心時,他隻可以 執行4個線程。
并發
想要了解并發,就需要知道 和 。
同步阻塞
當程式中的一個 I/O 操作,會占據比較長的時間,這時候,程式沒有被挂起,且一直在等待網絡資料傳輸,無法進行其他操作,這時候就是同步阻塞。
同步的一個概念就是,網絡傳輸完成後也無法告知主程式操作完成,這就導緻了主程式:
- 要麼隻能進行等待 I/O 完成
- 要麼輪詢去檢視是否傳輸是否已經完成
當然,輪詢時候可以進行其他的操作,這時候,就是非阻塞的狀态,即 同步非阻塞。
同步非阻塞
非阻塞的概念即主程式可以進行其他的操作。
異步阻塞
有同步,就有異步。
而異步阻塞與同步阻塞相同,主程式啥也不幹,就等着 I/O 操作完成。
異步非阻塞
異步非阻塞狀态,便是并發的關鍵。
當主程式使用異步 I/O 操作時,并不會影響主程式後續的運作,而當異步 I/O 操作完成後,會主動通知主程式進行其他操作,這樣就減少了輪詢過程中的資源消耗,專注于其他工作。
并發
而并發就是 異步非阻塞 狀态下的一種形式,當程式執行操作 a 時,使 a 的 I/O 異步操作,這時程式去執行操作 b, 在外部看來,a 和 b 時同時被執行的,然而他們隻運作在在一個線程當中。
與線程、程序不同的是,協程并不是作業系統實體層面存在的一種程式。
協程是程式級别的,由程式編寫者自己操控整個協程的生命周期。這樣就實作了類似作業系統操作多線程一樣的效果,但是省下了現成的切換中造成的資源消耗。
而通過程式來操縱協程,就造成了cpu 一直在運作,并且是多個協程一直在運作的假象,也就變成了并發。
執行個體
下面我們通過幾個執行個體來說明,在 python 中的程序、線程和協程的關系。
程序執行個體
在 python 中我們如何編寫多程序的程式呢?
答案是使用子產品 multiprocessing 進行實作。
import timefrom multiprocessing import Processclass Test(Process): def __init__(self): super().__init__() def run(self): while True: print("process b is run") time.sleep(1)
通過繼承 multiprocessing 的 Process ,實作程序類,然後實作 run 方法,即可在此方法中實作程序要運作的内容。
from process_b import Testimport timeif __name__ == "__main__": t = Test() t.start() while True: print("process a run") time.sleep(1)
調用方法也非常簡單,直接使用 Test 執行個體化對象,然後調用對象的成員函數 start() 即可。
python3 process_a.pyprocess a runprocess b is runprocess b is runprocess a runprocess a runprocess b is runprocess b is runprocess a run
線程執行個體
Cpython 中由于存在 GIL ,是以多線程在實際應用中也會變為單核 cpu 上的線程,排隊運作。
import threadingimport timeclass ThreadTest (threading.Thread): def __init__(self, name): super().__init__() self.name = name def run(self): while True: print(f"i am in thread {self.name}") time.sleep(1)if __name__ == "__main__": threads = [] for i in range(4): t = ThreadTest(i) threads.append(t) for t in threads: t.start() for t in threads: t.join()
通過繼承 threading.Thread 來實作線程類,然後通過執行個體化,生成對象,調用對象的 start() 即可啟動線程。
運作結果
python3 thread_a.pyi am in thread 0i am in thread 1i am in thread 2i am in thread 3i am in thread 1i am in thread 3i am in thread 0i am in thread 2i am in thread 1i am in thread 3i am in thread 0i am in thread 2i am in thread 1
協程
python3 将 asyncio 加入到了标準庫。
import asyncioimport timeasync def test(num): await asyncio.sleep(num) print(num)async def run(): tasks = [asyncio.create_task(test(num)) for num in range(4)] [await t for t in tasks]def run_main(): asyncio.run(run())if __name__ == "__main__": run_main()
運作結果
import asyncioimport timeasync def test(num): await asyncio.sleep(num) print(num)async def run(): tasks = [asyncio.create_task(test(num)) for num in range(4)] [await t for t in tasks]def run_main(): asyncio.run(run())if __name__ == "__main__": run_main()
總結
以上就是本節的所有内容,主要簡單地講解了關于 程序、線程和協程 的概念和例子。