計算機科學

程序 vs 執行緒:技術面試第一熱門問題,用記憶體結構徹底理解

開啟工作管理員會看到程序清單,閱讀開發文件時又常提到執行緒。兩者看起來都是「正在執行的某種東西」,但確切差異是什麼?

閱讀 4 分鐘
程序 vs 執行緒:技術面試第一熱門問題,用記憶體結構徹底理解 封面圖

開啟工作管理員會看到程序清單,閱讀開發文件時又常提到執行緒。兩者看起來都是「正在執行的某種東西」,但確切差異是什麼?

如果只能選一個最常出現在技術面試的問題,絕對就是這個:「請說明程序與執行緒的差異。」

它成為經典問題的原因很簡單:一題就能連續確認記憶體結構、作業系統和並行處理。

本文會從記憶體結構的角度整理兩個概念的差異,也會說明面試時回答到什麼程度最合適。

先從重點開始整理。

  1. 程序(Process):正在執行的程式。會取得完整且獨立的記憶體空間
  2. 執行緒(Thread):程序內的執行流程。只有堆疊各自獨立,其餘部分共用
  3. 因為共用,執行緒輕量又快速;也因為共用,會產生資料競爭
  4. 程序彼此隔離而安全,但相對沉重,程序間通訊也較繁瑣

程序:正在執行的程式

程式是儲存在磁碟上的一段程式碼,目前還沒有執行任何工作。

執行程式的瞬間,作業系統會將它載入記憶體,並準備分配 CPU。這個「正在執行的程式」就是程序。

作業系統會為每個程序提供完整且獨立的虛擬記憶體空間,整體大致分成四個區域。

區域 內容
程式碼(Text) 要執行的機器碼
資料(Data) 全域變數、靜態變數
堆積(Heap) 執行期間動態配置的記憶體
堆疊(Stack) 函式呼叫資訊、區域變數

這裡重要的是「獨立」這個詞。程序 A 無法查看程序 B 的記憶體,作業系統會從根本上阻擋這種存取。

因此,即使一個程序終止,其他程序仍能正常運作。Chrome 讓每個分頁使用獨立程序就是這個原因;即使一個分頁當掉,整個瀏覽器仍能存活。


執行緒:程序內的執行流程

執行緒是程序內實際執行程式碼的流程單位。每個程序至少會從一個執行緒(主執行緒)開始。

關鍵在於共用範圍。同一程序內的執行緒共用程式碼、資料和堆積區域,只有堆疊各自獨立。

只有堆疊各自獨立,程式碼、資料和堆積共用的結構
只有堆疊各自獨立,程式碼、資料和堆積共用的結構

堆疊需要各自獨立的原因很明確。堆疊記錄「目前執行到哪個函式的哪個位置」,因此每個執行流程都需要一個堆疊。

另一方面,由於堆積是共用的,執行緒之間只要透過同一個變數就能直接交換資料,不需要額外的通訊流程。


為什麼使用執行緒:成本問題

你可能會想:「想同時做多件事的話,啟動多個程序不就好了嗎?」但問題在於成本。

建立成本。建立程序必須準備完整的獨立記憶體空間;執行緒只要多配置一個堆疊即可。

切換成本。CPU 切換到另一個程序時,必須替換整份記憶體對映(context switching)。同一程序內的執行緒切換輕量得多。

通訊成本。程序間交換資料時,必須透過管線、socket 等 IPC(程序間通訊)流程;執行緒只要讀取同一個變數即可。

總而言之,執行緒是用較低成本同時處理多項工作的單位。


並非沒有代價:資料競爭

執行緒的所有優點都來自「共用記憶體」,而最大的弱點也正好來自同一點。

兩個執行緒同時寫入同一個變數時,結果就無法預測。這就是資料競爭。

即使是 count += 1 這種單行程式碼,內部也分成讀取 → 加法 → 寫入三個步驟;兩個執行緒重疊執行時,實際上可能發生遞增遺失。

因此需要鎖定、信號量等同步工具;但使用不當又會開啟另一個地獄:死結。

程序從一開始就隔離記憶體,因此沒有這類問題。這就是安全性與效率之間的取捨。

多個執行緒同時碰觸共用記憶體的瞬間,資料競爭就開始了
多個執行緒同時碰觸共用記憶體的瞬間,資料競爭就開始了

從 iOS 開發者的角度來看

在 iOS 中,一個 App 就是一個程序。受到沙盒政策限制,App 無法存取其他 App 的記憶體;上面提到的程序隔離會直接套用。

App 內由主執行緒負責 UI,網路工作和繁重計算則交給其他執行緒。「UI 更新要在主執行緒執行」這項規則正是建立在執行緒概念上。

不過,在現代 iOS 開發中,直接建立執行緒已不常見。將工作交給 GCD 的佇列或 Swift Concurrency 的 Task 後,系統會自動從執行緒集區分配。只是抽象層提升了一層,底層仍然是執行緒在運作。


面試時可以這樣回答

建議先從一句話的回答開始。

「程序是擁有獨立記憶體空間的執行單位;執行緒則是程序內的執行流程,只有堆疊各自獨立,其餘記憶體都共用。」

接下來的追問幾乎都能預測:「執行緒共用什麼、各自擁有什麼?」(只有堆疊)、「為什麼執行緒更輕量?」(建立、切換、通訊成本)、「共用會造成什麼問題?」(資料競爭與同步)。能回答到這裡就達到及格線。

再補上一個 Chrome 分頁或 iOS 沙盒之類的實際案例,就能讓面試官感覺你是理解,而不是死背。


總結

  • 程序是正在執行的程式,會從作業系統取得獨立的記憶體空間(程式碼、資料、堆積、堆疊)
  • 執行緒是程序內的執行流程,只有堆疊各自獨立,程式碼、資料和堆積則共用
  • 執行緒的建立、切換和通訊成本低,因此輕量;但由於共用記憶體,存在資料競爭風險
  • 程序彼此隔離而安全,但較沉重,通訊需要 IPC
  • 在 iOS 中,App 是一個程序,而 GCD 與 Task 是建構在執行緒上的抽象化
  • 面試回答骨架:一句話定義 → 共用範圍 → 成本差異 → 資料競爭