執行緒與並行

主執行緒、join 與 detach(main thread, joining, detaching)

每個程式啟動時就已經有一條執行緒在跑——主執行緒(main thread),那條從 main() 開始的控制流。當這個程式建立更多執行緒時,主執行緒就成了某種協調者,於是出現一個實際問題:當一條工人執行緒結束時,誰來幫它善後,又有沒有人等它的結果?兩個答案是 join(等待)與 detach(分離),你建立的每條執行緒最終都該被其中之一處理掉。

join 意思是等待。當你對一條執行緒呼叫 pthread_join() 時,你的執行緒會阻塞,直到那條執行緒結束,然後收回它的回傳值並讓系統回收它的資源。這是你說「在我繼續之前,我需要這名工人做完,而且我要它的結果」的方式。detach 意思是放手。當你對一條執行緒呼叫 pthread_detach()(或一開始就把它建立成分離的)時,你宣告你永遠不會去 join 它;系統會在它一結束就自動清理它的資源,你既不等它、也拿不到它的回傳值。對於不需要結果的「射後不理」背景工作就用 detach。這個配對很重要:一條可 join 的執行緒,若既沒被 join 也沒被 detach,結束時會洩漏它的記帳資源——一個小但真實的資源洩漏,是殭屍行程在執行緒上的對應物。

為何這在程式結束時也重要:當主執行緒從 main() 返回(或任何執行緒呼叫 exit())時,整個行程立刻結束,不管做完沒做完都猛然殺掉其他每一條執行緒。所以若主執行緒衍生了工人、卻沒 join 它們就從 main() 末端掉出去,那些工人可能在任務做到一半時被切斷、工作只做了一半、也從未被清理。慣常的紀律是:主執行緒建立工人,在結束前 join 每一條它在意的工人,然後才返回——保證工人真的做完了。(一條應該活得比一切都久的執行緒,例如背景日誌記錄器,通常改用 detach。)

一個程式把一個總和切到四條工人執行緒,然後對這四條都呼叫 pthread_join()。只有在第四次 join 返回後——也就是四條都確定做完並存好各自的部分和——主執行緒才把各部分加起來並印出總和。

要結果就 join;射後不理就 detach。從 main() 返回會一次殺掉所有執行緒。

一條執行緒必須「要嘛被 join、要嘛被 detach」,不能兩者都做、也不能兩者都不做——對一條已分離的執行緒做 join 是錯誤,而兩者都忘了則會洩漏資源。另外,同一條執行緒不能 join 兩次。一開始就決定好每條執行緒的歸宿。

又稱
main threadjoinable vs detached等待與分離執行緒主執行緒