cpu

Process, Thread, Coroutine 是什麼?

前言

在軟體開發的過程中,常會遇到需要提升效能、增加速度的情況,此時想到的解法除了優化程式碼外,常使用的就是 Asynchronous, Concurrency, Parallelism 的技巧吧 。即使不太了解背後的理論,相信大家也天天都在用他們。

但老實說,一半以上的問題都是 Code 寫不好造成的,先好好優化可能比較重要😢

但在直接進入探討 Asynchronous, Concurrency, Parallelism 前,Process, Thread, Coroutine 這些先備觀念還是得先建立。

以下本文會先介紹 Process, Thread, Coroutine 分別是什麼?以及一些衍生應該要知道的小知識。所提到的各種觀念,也會儘量用簡單的白話文來進行介紹,專有名詞的部份會保留英文和加上中文名稱。

而在另一篇文,我們已經介紹 Asynchronous, Concurrency, Parallelism 分別代表什麼意思,有興趣的朋友歡迎參考看看。

延伸閱讀

Program/ External Request/ Task

使用者使用滑鼠點擊圖示或鍵盤輸入指令後,電腦會接收到請求 (External Request),並開始執行程式以及程式內請求所相對應的任務 (Task)。

Program

又稱為程式。

程式可以是我們平常打開 IDE, Editor 後所撰寫的程式碼 (code),可以想像成只是單純存在在那邊的一些 “文字” ; 也可以是平常使用的瀏覽器、Line、Spotify…等。

而要怎麼撰寫 Program? 就是使用程式語言 (Programming Language) 來撰寫,如 Python, Swift, Golang…. 等,都是程式語言。

External Request

也就是外部調用、呼叫。

以下都是些 External Request 的例子:

  • 滑鼠點擊瀏覽器的圖示,開啟瀏覽器
  • 鍵盤輸入 go run xxxx.go 指令,執行某 go gile
  • A 程式在執行的過程中,呼叫 B 程式

External Request 可以是外部鍵盤、滑鼠傳給電腦的,也可以是 A 程式給 B 程式的,甚至 A 程式呼叫自己內部的 function 也可以。

收到 External Request 後,作業系統會產生任務,讓程式去執行。

Task

也就是任務。

類似平常使用程式語言撰寫的 Function ,將 1 個以上的 Function 組合成 Task。程式收到 External Request 後,程式內部會調用所需要的 Task。

Process/ CPU/ Thread

上一個部分介紹什麼是程式?以及如何產生任務

接著來介紹程式是怎麼執行,執行的過程中會發生什麼事情。

Process

Process 也可稱為 Application,中文常稱為程序、進程。

在學習程式語言的時候,相信大家都知道將程式執行後,會被轉成機器讀的懂的語言,也就是 Machine Code。接著,作業系統會分配部份記憶體出來,Machine Code 會載入到記憶體內,產生 Process。

換句話說,作業系統會分配記憶體資源給 Process。

Process 可視為是程式的實體,不同的 Process 所分配到的記憶體空間 (Memory Space) 是互相獨立的,因此 Process 之間不會共享資源 (ex: 記憶體、變數)。

要注意的是,在程式內可以依照需求,再創造出其他 Process,因此一個程式可以產生一個和以上的 Process。

以蓋房子來舉例

  • 建設公司 <–> 作業系統
  • 磚塊、水泥、鋼筋、怪手 <–> 記憶體資源
  • 建案 <–> Process

假設要蓋房子時,建設公司就會分配磚塊、水泥、鋼筋、怪手….等建設資源給這個建案。

同樣的,假設要執行瀏覽器程式,作業系統就會分配記憶體資源給瀏覽器 Process。

要注意!在此只提到 Process 被分配到記憶體資源,但程式還沒開始被執行喔 (房子還沒正式開始蓋,只是把磚塊、水泥….等準備好而已)

情境

  • 使用者用滑鼠點擊瀏覽器的圖示,作業系統接收到打開瀏覽器的任務,會分配部分記憶體,給瀏覽器這個程式產生的一個 Process 。
  • 使用者執行自己寫的網站爬蟲程式,作業系統接收到執行網站爬蟲程式的任務,會分別分配部分記憶體,給這個程式產生的兩個 Process 。

以上的各種情境,作業系統在接收到執行程式的任務後,會開始分配記憶體給 Process,替這個程式產生一個或一個以上的 Process。

延伸閱讀

CPU

上面提到 Process 只是被分配好記憶體資源的一個單元,程式尚未開始被執行。

電腦內還有另一個重要的東西叫做 CPU,Process 要被分派給 CPU 後,才可以讓 CPU 開始執行 Process 內的任務,而一個 CPU 一次只能執行一個 Process 上的任務。

現代電腦越來越複雜,可以做的事情也越來越多,任務也會越來越多樣,因此 CPU 內從過去的單核心慢慢演變到雙核心、多核心。而現代人使用電腦,不可能只執行單一程式,因此作業系統內部往往同時會有很多 Process 存在。

CPU 的核心數越多,作業系統可以同時處理的 Process 就越多,稱為 Multi-Processing 或 Multi-Tasking,同時使用多個核心來執行 Process 上的任務,可以讓電腦有更多工的感覺,一次處理更多任務。

延伸閱讀

cpu cores
雙核心的電腦

Thread

上面提到 Process 分派給 CPU 後, CPU 會開始執行 Process 內的任務,而靠的就是 Process 內的 Thread。

Thread 中文稱為執行緒、線程。

一個 Process 內可以有一個到多個 Thread,每個 Thread 可分別負責某幾個功能,也就是某些任務。開發程式時可以把這些功能要執行的任務開個 Thread 來負責執行。

pid is process id
pid 是 process 的編號,可以看到一個 pid 內可以有多個執行緒

例如:

  • 打開瀏覽器上網的時候,每一個分頁可以都是一個 Thread,每個 Thread 就是負責處理該分頁的畫面繪製、紀錄上一頁下一頁、是否要重新整理….各種功能。
  • iOS 開發中,所有關於 UI 繪製的任務都是在一個叫做 main thread 的 Thread 中進行。

Process 的記憶體、變數可以讓其下所有 Thread 共享,也就是所有 Thread 都可以存取 Process 的記憶體和變數。而 Thread 之間也會共享記憶體、變數。

Process 分派給 CPU 後,Process 上的 Thread 獲得 CPU 時間,此時 CPU 就會開始執行 Thread 內的任務。

要注意的是,Process 是作業系統分配記憶體資源的對象,而 Thread 是作業系統分配 CPU 時間的對象,有 CPU 時間,才可以執行 Thread 上的任務。

再以蓋房子當作例子:

  • 建設公司 <–> 作業系統
  • 磚塊、水泥、鋼筋、怪手 <–> 記憶體資源
  • 建案 <–> Process
  • 建築工人 <–> Thread

假設要蓋房子時,建設公司就會分配磚塊、水泥、鋼筋、怪手….等建設資源給這個建案。建築工人就會利用建設資源來開始蓋房子。

同樣的,假設要執行瀏覽器程式,作業系統就會分配記憶體資源給瀏覽器 Process。當作業系統將瀏覽器 Process 分配給 CPU 時, Process 上的 Thread 獲得 CPU 時間,CPU 就會開始執行 Thread 上的任務。

延伸閱讀

I/O/ Coroutine/ 任務

在上方曾經介紹過任務是什麼,在有了 Process 和 Thread 的概念後,再來詳述一次任務的各種類型。

I/O 任務

Input/Output,即輸入/輸出。

是資訊處理系統與外部世界之間的通訊。資訊處理系統可以是一台電腦,也可以是一個 CPU…等。外部世界可以是印表機、滑鼠、鍵盤,也可以是記憶體…等。

進行 I/O 操作的過程會經歷兩個階段(phase):

  1. 等待 Kernel 將資料準備就緒 (Waiting for the data to be ready),此時達到 Initial。
  2. 將資料從 Kernel 複製到 Process/ Thread ( Copying the data from the kernel to the process),此時達到 Complete。

因為 I/O 操作會需要經過兩個階段,由此可知,I/O 操作往往會耗費較長的時間,以下是常見的 I/O 操作:

  • 在程式中讀寫修改檔案
  • 網路請求

延伸閱讀

Coroutine

中文又稱協程、微線程。

Coroutine 也是任務 (Task),而 Coroutine 任務類似平常大家寫的 function,但又不像一般的 function,Coroutine 是可以隨時暫停執行,再從暫停的地方恢復執行的 function。

在 Python 中的 async, await, yield, generator…等,就是 Coroutine 很好的應用。

而在 Golang 中,除了使用 Coroutine 的機制暫停和恢復機制外,還另外加以優化,增加效能,在 Golang 之中稱為 goroutine。

延伸閱讀

任務

所以任務到底有哪些類型?到底是什麼?

一般來說,會探討任務的問題都是因為效能,也就是遇到比較耗費時間的任務,要想辦法提升效能和速度。

而比較耗費時間的任務常分成以下兩種類型:

  • CPU bound 的任務

CPU 比較吃重的任務,都是些很吃電腦效能的,例如:要跑很多迴圈和計算、算反矩陣…等計算量大的工作。

  • I/O bound 的任務

I/O 比較吃重的任務,例如:網路請求、檔案操作…等

既然提到了 I/O bound 的任務,大家不知道有沒有想到。上方提到 I/O 任務會分成兩個階段,而會分成兩個階段代表的意思是可以先完成第 1 階段,過段時間再完成第 2 階段,也就是兩個階段不一定要連續執行,是可以分開執行的。

推廣這個概念,Coroutine 是不是也是一樣呢?

執行到一半可以暫停,過段時間再繼續執行。差別只在 I/O 任務兩個階段的切分是依賴 kernel 安排的,而 Coroutine 的各個階段切分是開發者自己決定的,也就是由開發者決定何時暫停、何時繼續執行。

延伸閱讀

結論

相信 Process, Thread, Coroutine 是每個程式開發者都會遇到的東西,本文從作業系統開始,簡單的講解了 CPU, Program, 接著帶到 Process, Thread, Coroutine。

希望大家閱讀完後,可以對這些複雜的名詞有基本的認知!

而在另一篇文,會接著介紹 Asynchronous, Concurrency, Parallelism 分別代表什麼意思,有興趣的朋友歡迎參考看看。我們下次見~

延伸閱讀

發佈留言