執行緒 vs 行程(threads vs processes)
把它想成辦公室。行程(process)是一整間擁有自己大樓的公司——自己的上鎖房間、自己的檔案櫃、自己的大門。執行緒則是那棟大樓裡的一名員工。同一家公司的兩名員工自由共用所有房間和櫃子;來自不同公司的兩個人則得打電話或寄文件才能溝通。這幅畫面抓住了核心差別:同一行程裡的執行緒共享記憶體;不同的行程則不共享。
更精確地說:行程是一個執行中的程式,擁有自己私有的定址空間——自己對記憶體的視野,由作業系統與其他每個行程隔離開來。執行緒是行程內部的一條執行流,而行程裡的每條執行緒共享那一個定址空間:同一個堆積、同一批全域變數、同一份程式碼、同一批開啟的檔案。每條執行緒留給自己的東西不多但至關重要——自己的堆疊(放它的區域變數與呼叫鏈)以及自己的暫存器狀態,包括程式計數器。所以當執行緒 A 寫入一個全域變數或堆積物件時,執行緒 B 立刻看得到改變,因為它們看的是同一批位元組。兩個各自獨立的行程寫入「同一個」全域時,各自碰的是自己私有的副本,永遠不會相撞。
這為何重要:共享讓執行緒之間的溝通既快又方便(只要讀寫共享記憶體)但危險(兩條執行緒可能在同一筆資料上亂塗而把它弄壞——這正是本欄位的主題)。行程隔離讓行程彼此安全(一個行程的崩潰或亂寫碰不到另一個的記憶體)但較慢、也較難在行程間共享資料(你需要明確的行程間通訊)。在執行緒與行程之間選擇,就是在「便宜但有危害的共享」與「強隔離但有開銷」之間選擇。
瀏覽器可能讓每個分頁跑成獨立的行程,這樣一個崩潰的頁面就拖不垮其他分頁——強隔離。而在單一分頁內,好幾條執行緒(繪製、網路、指令稿)共享該分頁的記憶體以快速協作。
用行程求隔離,用執行緒求便宜的共享——瀏覽器刻意兩者並用。
行程裡的兩條執行緒都共享堆積與全域,但各自有「自己的」堆疊——所以某條執行緒裡的區域變數是私有的。把指向執行緒 A 堆疊上某個區域變數的指標傳給執行緒 B 是危險的,因為一旦 A 的函式返回,那個堆疊框就不在了。