屏障與閂(barrier 與 latch)
想像一場團體健行,每到一個路標都得全員集合才走下一段:腳程快的先到就等,直到最後一個人現身,整團才一起出發。屏障就是執行緒的這個集合點:一個同步點,在固定數量的執行緒全都抵達之前,沒有任何執行緒能通過,而在那一刻它們一起被放行。
屏障是為一個已知的參與者數量(比如 N)建立的。每個執行緒抵達屏障時呼叫 wait(例如 pthread_barrier_wait)。最先到的 N-1 個執行緒阻塞;當第 N 個抵達時,屏障「觸發」,全部 N 個執行緒被放行繼續。經典用途是逐階段的平行計算:把工作切給 N 個執行緒,讓它們全都完成第一階段並在屏障會合,然後全部一起開始第二階段——保證沒有任何執行緒在每個執行緒都產出第一階段結果之前,就讀到第二階段的資料。閂(如倒數閂)是它的近親,用於一次性的協調:它初始化成一個計數,執行緒可以等在它上面,而其他事件把它倒數;當計數歸零時閂打開並維持打開,放行所有等待者。關鍵差別在於可重用性與方向:屏障是對稱的(等待的就是那 N 個抵達的工人),通常可重用於多個階段;而閂是單向、一次性的——一旦打開就不能再關——且把它倒數的執行緒不必是等在它上面的執行緒(一個執行緒可以等待其他好幾個完成初始化)。
凡是你需要「所有人都到這點之前誰都別往前」(屏障)或「等到這 N 件事都發生」(閂)的地方,它們就會出現。屏障適合迭代式模擬與帶同步階段的平行迴圈;閂適合啟動把關(「在伺服器初始化完成前壓住所有工人」)與類似 join 的等待(「主執行緒等 5 個初始化任務回報完成」)。屏障常見的錯誤是「建立時用的計數」與「實際呼叫 wait 的執行緒數」不符——太少就全部永遠阻塞;太多就讓多出來的那些要嘛報錯、要嘛意外地滾進下一階段。
pthread_barrier_init(&b, NULL, N);,每個工人:compute_phase1(); pthread_barrier_wait(&b); compute_phase2();。在全部 N 個都完成第一階段之前,沒有執行緒會開始第二階段。閂則相反:主執行緒等在一個計數 5 上,每個初始化任務各把它減一次。
屏障在會合點把 N 個一起放行;閂在計數歸零時打開一次。
屏障會阻塞,直到剛好「設定數量」的執行緒呼叫 wait——只要少來一個(某執行緒提早死掉或走了不同路徑),其餘的就永遠卡住。閂是一次性的:它倒數、打開一次、不能重設,所以當你需要反覆同步時,要用屏障而非閂。