Multiple Process v.s Multiple Thread v.s Coroutine

571
Multiple Process v.s Multiple Thread v.s Coroutine

在計算機領域中,我們經常遇到進程(Process)、多進程(Multiple Process)、線程(Thread)、協程(Coroutine)等關鍵詞,又到底什麼是Multiple Processing、什麼是Multiple Threading呢、什麼是Coroutine呢? 這些術語究竟有何不同

透過本文,我們將帶您深入了解它們之間的差異,從而更好地理解計算機科技領域的核心概念。

簡介

一般運行程式時,最直覺的方式是一行一行的執行,但很多時候我們會遇到某一行程式碼卡很久並導致整個application都因此卡住不動,要是讓老闆等到茶都涼了,那我這份工作可能也涼了🤣,因此聰明的開發者就針對這類型的按力發展出了許多解決方案,以下將會介紹我們有哪些好方法可以解決這類型的問題。

I/O bound v.s CPU bound

首先,程式再執行任務時,會分成I/O bound和CPU bound

圖一: 程式運行時的圖示

  • I/O bound
    任務本身是透過網際網路去傳送請求,這時就會受到寫入/讀取的速度限制,比如打API到遠端的Server獲取股票價格、上傳高畫質相片到雲端會根據電腦的網路速度、以及其他任何牽涉到網路讀取和寫入,都屬於I/O bound的一種。
  • CPU bound
    任務本身涉及大量CPU、GPU計算,比如3D製圖、近期很火紅的AI模型訓練、本地解壓縮檔案等都屬於CPU bound的一種。

圖二:Program & Process & Thread 示意圖

如上圖所示,從上到下分別是簡單的單位區分,我們運行的程式碼就是Program,當你啟動某應用程式,實際上就是讓Program生成進程(Process)並執行程式,一個Program可以開出多個進程(Process),每條進程都有一個獨立的id(PID),而一個進程(Process)可以開出多個線程(Thread),線程即為電腦運行軟體的最小單位,是程式碼實際實行的地方,並且Thread之間可以共享記憶體,統一由Process管理。

什麼是Multiple Processing

每個Process擁有自己的記憶體位址,並且是彼此獨立的。

Multiple Processing是使用兩個以上的CPU來再同時間執行獨立的任務,Process彼此之間不會互相影響,理論上電腦上的每顆CPU只可以開出一條新的process,所以假設你的電腦是八核心,那你的電腦至多能開出小於等於8的Process。

應用案例: 比如電影渲染時會將不同的區塊分配給不同的核心來加速渲染速度。

什麼是Multiple Threading

Multiple Threading 是再相同的Process內開啟多個Threads來做到平行執行任務,再運行程式時,Threads可以從動態產生的共用記憶體(heap)中調用變數並再runtime(執行程式)時存放於stack,多個Thread雖然可以同時進Process存取資料,但是操作系統只會依次分配時間片給先來報名存取的Thread,俗稱Scheduling

應用案例: 比如你此刻打開的多個網站都是由,他們的parent process可能是一樣的,只是每個分頁由不同的thread來處理。

什麼是Coroutine

協程(Coroutine)讓我們可以在”單線程”的情況下達到類似多線程的效果,相較於線程更小的執行單位,屬於純應用層的執行單位,作業系統並不知道有它的存在,但它和線程一樣有自己的記憶體空間,要注意的是Coroutine的目的只是讓非阻塞的操作能夠被同步執行,如果程式本身是CPU bound的話那就一樣會被阻塞住!

Python AsyncIO

AsyncIO 的概念最早起源於 JavaScript 的 async/await 語法。它在單線程中實現了類似於Multiple Threading的效果,採用類似 Coroutine 的方式在背景執行。由事件循環(event loop)在有空閒時檢查各個背景任務是否有返回結果,適合處理涉及網絡傳輸的 I/O bound 任務,例如 API 伺服器。Server首先接受多個使用者的請求,然後等到處理完後自動回傳給使用者。若API Server不支援 AsyncIO的話,可能會導致對使用者來說的請求處理時間過長,遲遲沒有等不到回應。

為何要在Python裡使用AsyncIO而不是用Multiple Threading?

由於受限Python 全域鎖GIL(Global Interpreter Lock) 的特性,當我們打算再Python運行Multiple Threading的程式碼時所有的Thread都會被鎖住,導致沒辦法執行,效果相當於Single-threaded,幸運的是,AsyncIO再Python3.4開始支援,Python官方對AsyncIO的解釋如下

<em>asyncio is a library to write concurrent code using the async/await syntax.
asyncio is used as a foundation for multiple Python asynchronous frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc.
asyncio is often a perfect fit for IO-bound and high-level structured network code.</em>

註:雖然 Python 仍有支援執行緒的庫,但效能不如 AsyncIO

Goroutine (Go的Coroutine)

  • Goroutine是 Golang中的一個核心特性,它是一個輕量級的執行緒,由 Go 語言的執行時環境(runtime)管理,不同於傳統的執行緒,Go協程的開銷非常小,可以同時運行成千上萬個協程。在函數調用前加上 go 關鍵字來非阻塞地啟動一個新的協程,內建的調度器來管理協程的執行。調度器會動態分配協程給不同的執行緒,實現了高效的並行操作。

同步 (Synchronous) V.S 異步 (Asynchronous)

最後總結一下同步和異步的具體區別

  • 同步 Synchronous
    在同步操作中,程式的執行是按照順序一行一行進行的。每一行程式必須完成後,才會繼續執行下一行。儘管這種方式確保了操作的順序性,但在等待一些耗時操作(例如網絡請求或文件讀取)完成時,可能導致等待時間過長,從老闆的角度來看,這似乎有點在浪費時間。
  • 異步 Asynchronous
    異步操作不僅適用於多線程(Multiple Threading)或多進程(Multiple Processing)的環境,同樣適用於單執行緒(Single Thread)或單進程(Single Process)的情況。當某一行程式(前提必須是 I/O bound 的操作)的等待時間過長時,我們可以採用異步方式,使該操作在背景中執行,同時主程式(main)可以繼續執行其他任務。為實現此目的,我們需要有共享記憶體空間以儲存變數,或者使用一個中控台來管理在背景中執行的任務。當任務完成時,我們可以通知主程式。常見的異步機制包括回調(callback)、Promise、Future 以及 async/await 等。

結語

以上就是本次的分享。以下幫大家總整理幾個重點總結

  • Multiple Processing 多進程 – Process之間彼此獨立不互相影響,直接影響OS系統
  • Multiple Threading 多線程 – Thread之間共享相同Process的記憶體
  • Concurrent 並發 – 並發只是一個概念,它可以被套用到異步或是多線程內,只是實作方式不同
  • Asynchronous 異步 – 常見的方式如Python的AsyncIO、NodeJS和C#的Async/Await、Golang的Goroutine,實作方式雖然不同,但都是為了在單線程的環境下更有效率的處理 I/O bound 任務以實現類似多執行緒效果。透過圖片希望能幫助你更好的瞭解其中的差異。

  • 實際比較
    想像一個大型電子商務平台處理訂單的情景。使用多進程可以將不同的訂單處理在不同的進程中,確保一個訂單處理的錯誤不會影響其他訂單。多線程則可以在處理每個訂單的同時執行多個子任務,例如庫存更新、付款處理等。異步處理則可以確保在高峰時段處理訂單時,伺服器不會因為等待外部系統回應而陷入阻塞。

到這邊就是整篇文章的介紹啦,喜歡的話歡迎分享給有一樣疑問的同學們唷~

參考連結

Difference between Multi-tasking and Multi-threading

LEAVE A REPLY

Please enter your comment!
Please enter your name here