進程、線程與協程

在操作系統中,每一個獨立運行的程序,都佔有 操作系統 分配的資源,這些程序中間互不干涉,都只負責運行自己的程序代碼,這就是進程。

Python - 進程、線程與協程

但是當操作系統頻繁的創建銷燬進程時,大量的系統資源被浪費在創建和銷燬的過程中。而隨著多核心 cpu 的出現,線程也逐漸代替了進程,成為了操作系統 可以獨立運行的基本單位。

Python - 進程、線程與協程

當進程不是多線程程序時,存在於進程當中的唯一線程,便是進程本身運行的代碼塊。

而當多線程出現在一個進程中時,則多個線程之間共享此進程的資源,並接受操作系統的調度來運行每個線程。

狀態

Python - 進程、線程與協程

協程

為了瞭解協程的概念,我們先來了解一下併發和並行。

並行

並行比較好理解,即有多個程序在同時執行,這裡的程序指的是操作系統的線程。

每個 cpu 核心,只能在同一個時刻運行一組指令,意味著同一時刻,一個核心上只有一個線程在執行。

Python - 進程、線程與協程

當 cpu 有四個核心時,他只可以 執行4個線程。

併發

想要了解併發,就需要知道 和 。

同步阻塞

當程序中的一個 I/O 操作,會佔據比較長的時間,這時候,程序沒有被掛起,且一直在等待網絡數據傳輸,無法進行其他操作,這時候就是同步阻塞。

Python - 進程、線程與協程

同步的一個概念就是,網絡傳輸完成後也無法告知主程序操作完成,這就導致了主程序:

  • 要麼只能進行等待 I/O 完成
  • 要麼輪詢去查看是否傳輸是否已經完成

當然,輪詢時候可以進行其他的操作,這時候,就是非阻塞的狀態,即 同步非阻塞。

同步非阻塞

非阻塞的概念即主程序可以進行其他的操作。

Python - 進程、線程與協程

異步阻塞

有同步,就有異步。

而異步阻塞與同步阻塞相同,主程序啥也不幹,就等著 I/O 操作完成。

Python - 進程、線程與協程

異步非阻塞

異步非阻塞狀態,便是併發的關鍵。

當主程序使用異步 I/O 操作時,並不會影響主程序後續的運行,而當異步 I/O 操作完成後,會主動通知主程序進行其他操作,這樣就減少了輪詢過程中的資源消耗,專注於其他工作。

Python - 進程、線程與協程

併發

而併發就是 異步非阻塞 狀態下的一種形式,當程序執行操作 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>

總結

以上就是本節的所有內容,主要簡單地講解了關於 進程、線程和協程 的概念和例子。


分享到:


相關文章: