目錄
1.阻塞(Blocking)
2.阻塞 VS 輪詢
3.線程狀態
????????到目前為止,我們已經闡述了如何在線程上啟動任務、配置線程以及實現雙向數據傳遞。同時,我們也說明了局部變量是線程私有的,而引用可以通過共享字段在線程間傳遞以實現通信。
下一步的關鍵是同步機制:通過協調線程行為來獲得可預測的結果。當多個線程訪問同一數據時,同步顯得尤為重要——這個領域看似簡單卻暗藏風險。
同步構造可分為四大類別:
-
簡單阻塞方法
這類方法通過等待其他線程結束或計時完成來實現同步。例如:Sleep
、Join
?和?Task.Wait
?都屬于簡單阻塞方法。 -
鎖定構造
用于限制同時執行某段代碼或操作的線程數量。最常見的獨占鎖(如?lock
/Monitor.Enter
/Monitor.Exit
、Mutex
?和?SpinLock
)僅允許一個線程進入,確保競爭線程訪問共享數據時互不干擾。非獨占鎖包括信號量(Semaphore
/SemaphoreSlim
)和讀寫鎖。 -
信號構造
允許線程暫停運行直至接收到其他線程的通知,從而避免低效的輪詢。常用信號機制有兩種:事件等待句柄(event wait handles)和?Monitor
?的?Wait
/Pulse
?方法。.NET Framework 4.0 新增了?CountdownEvent
?和?Barrier
?類。 -
非阻塞同步構造
通過調用處理器原語來保護共享字段的訪問。CLR 和 C# 提供的非阻塞構造包括:Thread.MemoryBarrier
、Thread.VolatileRead
、Thread.VolatileWrite
、volatile
?關鍵字以及?Interlocked
?類。
除最后一類外,阻塞機制在其他類別中均占核心地位。下面我們將簡要探討這一概念。
1.阻塞(Blocking)
????????當線程因某些原因暫停執行時(例如通過?Sleep
?進入休眠,或通過?Join
/EndInvoke
?等待其他線程結束),該線程即被視為阻塞狀態。阻塞線程會立即釋放其處理器時間片,此后在阻塞條件滿足前不再消耗任何處理器資源。可通過線程的?ThreadState
?屬性檢測阻塞狀態:
bool blocked = (someThread.ThreadState & ThreadState.WaitSleepJoin) != 0;
(由于線程狀態可能在檢測與后續操作之間發生變化,此代碼僅適用于診斷場景。)
當線程阻塞或解除阻塞時,操作系統會執行上下文切換。這將產生幾微秒的開銷。
線程會通過以下四種方式解除阻塞(當然,按電腦電源鍵不算!):
-
阻塞條件得到滿足
-
操作超時(如果指定了超時時間)
-
通過Thread.Interrupt被中斷
-
通過Thread.Abort被中止
需要注意的是,如果線程是通過(已棄用的)Suspend方法暫停執行的,則不會被判定為處于阻塞狀態。
2.阻塞 VS 輪詢
當線程需要暫停直到特定條件滿足時,通常有兩種實現方式:
-
阻塞等待(高效方式)
通過信號和鎖機制實現,線程會進入阻塞狀態直到條件滿足,此時操作系統會調度其他線程執行。 -
輪詢等待(簡單但低效)
線程通過循環檢測條件來實現等待,例如:
while (!proceed); // 忙等待
或
while (DateTime.Now < nextStartTime); // 時間等待
性能考量:
-
純輪詢會完全占用CPU資源,因為CLR和操作系統會認為線程正在進行重要計算
-
這種方式的CPU利用率是100%,極其浪費系統資源
改進方案:
可以采用混合阻塞的方式:
while (!proceed) Thread.Sleep(10); // 每次檢查后休眠10ms
雖然不夠優雅,但相比純輪詢能顯著降低CPU占用率。不過需要注意共享變量(如proceed標志)的并發訪問問題,正確的鎖和信號量使用可以避免這些問題。
適用場景:
當預期條件能在極短時間內(如幾微秒)滿足時,短暫輪詢可能更高效,因為它避免了上下文切換的開銷和延遲。.NET框架為此提供了專門的工具類和方法,這些內容將在并行編程章節詳細介紹。
這里小節一下:
-
阻塞等待:適合大多數情況,資源利用率高
-
純輪詢:簡單但資源浪費嚴重
-
混合模式:折中方案,需要處理并發問題
-
微秒級等待:特殊場景下短暫輪詢可能更優
3.線程狀態
????????您可以通過ThreadState屬性查詢線程的執行狀態。該屬性返回一個ThreadState類型的標志枚舉,它以位運算方式組合了三個"層次"的數據。不過,大多數枚舉值都是冗余的、未使用的或已廢棄的。下圖展示了其中一個"層次":
以下代碼可將ThreadState精簡為四個最常用的狀態值:未啟動(Unstarted)、運行中(Running)、等待休眠或加入(WaitSleepJoin)和已停止(Stopped):
public static ThreadState SimpleThreadState(ThreadState ts)
{return ts & (ThreadState.Unstarted |ThreadState.Running |ThreadState.WaitSleepJoin |ThreadState.Stopped);
}
ThreadState屬性適用于診斷目的,但不適合用于同步控制,因為在檢測線程狀態和基于該狀態執行操作之間,線程狀態可能會發生變化。
本節完,下一節將開始介紹同步工具