Python - 進程、線程與協程

Python - 高級教程 - 進程、線程與協程

進程與線程

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

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

當進程不是多線程程序時,存在於進程當中的惟一線程,即是進程自己運行的代碼塊。網絡

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

狀態

協程

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

並行

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

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

cpu 有四個核心時,他只能夠並行執行4個線程。async

併發

想要了解併發,就須要知道阻塞異步函數

同步阻塞

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

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

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

固然,輪詢時候能夠進行其餘的操做,這時候,就是非阻塞的狀態,即 同步非阻塞。

同步非阻塞

非阻塞的概念即主程序能夠進行其餘的操做。

異步阻塞

有同步,就有異步。

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

異步非阻塞

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

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

併發

而併發就是 異步非阻塞 狀態下的一種形式,當程序執行操做 a 時,使 a 的 I/O 異步操做,這時程序去執行操做 b, 在外部看來,a 和 b 時同時被執行的,然而他們只運行在在一個線程當中。

與線程、進程不一樣的是,協程並非操做系統物理層面存在的一種程序。

協程是程序級別的,由程序編寫者本身操控整個協程的生命週期。這樣就實現了相似操做系統操做多線程同樣的效果,可是省下了現成的切換中形成的資源消耗。

而經過程序來操縱協程,就形成了cpu 一直在運行,而且是多個協程一直在運行的假象,也就變成了併發。

實例

下面咱們經過幾個實例來講明,在 python 中的進程、線程和協程的關係。

進程實例

python 中咱們如何編寫多進程的程序呢?

答案是使用模塊 multiprocessing 進行實現。

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)

複製代碼

經過繼承multiprocessingProcess,實現進程類,而後實現 run 方法,便可在此方法中實現進程要運行的內容。

from process_b import Test
import time

if __name__ == "__main__":
    t = Test()
    t.start()
    while True:
        print("process a run")
        time.sleep(1)


複製代碼

調用方法也很是簡單,直接使用 Test 實例化對象,而後調用對象的成員函數start() 便可。

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
複製代碼

線程實例

Cpython 中因爲存在 GIL,因此多線程在實際應用中也會變爲單核cpu 上的線程,排隊運行。

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()
複製代碼

經過繼承 threading.Thread 來實現線程類,而後經過實例化,生成對象,調用對象的 start() 便可啓動線程。

運行結果

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
複製代碼

協程

python3asyncio 加入到了標準庫。

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()
複製代碼

運行結果

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()
複製代碼

總結

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

相關文章
相關標籤/搜索