在操作系統中,每一個獨立運行的程序,都佔有 操作系統 分配的資源,這些程序中間互不干涉,都只負責運行自己的程序代碼,這就是進程。
但是當操作系統頻繁的創建銷燬進程時,大量的系統資源被浪費在創建和銷燬的過程中。而隨著多核心 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 進行實現。
<code>import time
from multiprocessing import Process
class Test(Process):
def __init__(self):
super().__init__()
def run(self):
while True:
print("process b is run")
time.sleep(1)/<code>
通過繼承 multiprocessing 的 Process ,實現進程類,然後實現 run 方法,即可在此方法中實現進程要運行的內容。
<code>from process_b import Test
import time
if __name__ == "__main__":
t = Test()
t.start()
while True:
print("process a run")
time.sleep(1)/<code>
調用方法也非常簡單,直接使用 Test 實例化對象,然後調用對象的成員函數 start() 即可。
<code>python3 process_a.py
process a run
process b is run
process b is run
process a run
process a run
process b is run
process b is run
process a run/<code>
線程實例
Cpython 中由於存在 GIL ,所以多線程在實際應用中也會變為單核 cpu 上的線程,排隊運行。
<code>import threading
import time
class 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()/<code>
通過繼承 threading.Thread 來實現線程類,然後通過實例化,生成對象,調用對象的 start() 即可啟動線程。
運行結果
<code>python3 thread_a.py
i am in thread 0
i am in thread 1
i am in thread 2
i am in thread 3
i am in thread 1
i am in thread 3
i am in thread 0
i am in thread 2
i am in thread 1
i am in thread 3
i am in thread 0
i am in thread 2
i am in thread 1/<code>
協程
python3 將 asyncio 加入到了標準庫。
<code>import asyncio
import time
async 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()/<code>
運行結果
<code>import asyncio
import time
async 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()/<code>
總結
以上就是本節的所有內容,主要簡單地講解了關於 進程、線程和協程 的概念和例子。
閱讀更多 sandag 的文章