c#非同步程式設計基礎
阿新 • • 發佈:2020-11-24
執行緒(Thread)
什麼是執行緒Thread
- 執行緒是一個可執行路徑,它可以獨立於其他執行緒執行.
- 每個執行緒都在作業系統的程序(Process)內執行,而作業系統程序提供了程式執行的獨立環境.
- 單執行緒應用,在程序的獨立環境裡只跑一個執行緒,所以該執行緒擁有獨佔權.
- 多執行緒應用,在單個程序中會跑多個執行緒,他們會共享當前的執行環境(尤其是記憶體)
執行緒的一些屬性:
- 執行緒一旦開始執行,IsAlive就是True,執行緒結束就變成false.
- 執行緒結束的條件:執行緒建構函式傳入的委託結束了執行.
- 執行緒一旦結束,就無法重啟.
- 每個執行緒都有個Name屬性,通常用於除錯.執行緒Name只能設定一次,以後更改會丟擲異常.
- 靜態的Tread.CurrentThread屬性,會返回當前執行的執行緒.
執行緒的Join and Sleep
- 呼叫Join方法,就可以等待另一個執行緒結束.
- 呼叫Join的時候,可以設定一個超時,用毫秒或者TimeSpan都可以.
- 返回一個bool型別,如果返回true,那就是執行緒結束;如果返回false,那就表示超時了.
- 呼叫Sleep方法會暫停當前的執行緒,並等待一段時間.
注意:
-
- Thread.Sleep(0)這樣呼叫會導致執行緒立即放棄當前的時間片,自動將CPU移交給其他的執行緒.
- Thread.Yield()做同樣的事情,但是它只會把執行交給同一處理器上的其他執行緒.
- 當等待Sleep或Join的時候,執行緒處於阻塞的狀態.
執行緒的阻塞
- 如果執行緒的執行由於某種原因導致暫停,那麼就認為該執行緒被阻塞了.例如在Sleep或通過Join等待其他執行緒結束.
- 被阻塞的執行緒會立即將處理器的時間片生成給其他執行緒,從此就不再消耗處理器時間,直到滿足其阻塞條件為止(然後cpu的時間片會返回到本執行緒).
- 可以通過ThreadState這個屬性來判斷執行緒是否處於被阻塞的狀態.
bool blocked=(someThread.ThreadState & ThreadState.WaitSleepJoin)!=0;
ThreadState
- ThreadState是一個flags enum,通過按位的形式,可以合併資料的選項.
- 下圖是Thread有可能處於的狀態.但是它大部分的列舉值都沒有什麼用.四個最有用的值:Unstarted,Running,WaitSleepJoin和Stopped.
解除阻塞Unblocking
- 當遇到下列四種情況的時候,就會解除阻塞:
- 阻塞條件被滿足
- 操作超時(如果設定超時的話)
- 通過Thread.Interrupt()進行打斷
- 通過Thread.Abort()進行中止
上下文切換
- 當執行緒阻塞或解除阻塞時,作業系統將執行上下文切換.這會產生少量開銷,通常為1或2微秒.
I/O-bound vs Compute-bound(或CPU-Bound)
- 一個花費大部分時間等待某事發生的操作稱為I/O-bound
- I/O繫結操作通常涉及輸入或輸出,但這不是硬性要求:Thread.Sleep()也被視為I/O-bound
- 相反,一個花費大部分時間執行CPU密集型工作的操作稱為Compute-bound
阻塞 vs 忙等待(自旋)
Blocking vs Spinning
- I/O-bound操作的工作方式有兩種:
- 在當前執行緒上同步的等待
- Console.Readline(),Thread.Sleep(),Thread.Join()...
- 非同步的操作,在稍後操作完成時觸發一個回撥動作.
- 在當前執行緒上同步的等待
- 同步等待的I/O-bound操作將大部分時間花在阻塞執行緒上.
- 他們也可以週期性的在一個迴圈裡進行"打轉(自旋)"
while(DateTime.Now<nextStartTime) Thread.Sleep(100);
while(DateTime.Now<nextStartTime)
- 在忙等待和阻塞方面有一些細微的差別
- 首先,如果你希望條件很快得到滿足(可能在幾微秒之內),則短暫的自旋可能會很有效,因為它避免了上下文切換的開銷和延遲.
- .NET Framework提供了特殊的方法和類來提供幫助SpinLock和SpinWait
- 其次,阻塞也不是零成本.這是因為每個執行緒在生存期間會佔用大約1MB的記憶體,並會給CLR和作業系統帶來持續的管理開銷.
- 因此,在需要處理成百上千個併發操作的大量I/O-bound程式的上下文中,阻塞可能會很麻煩
- 所以,此類程式需要使用基於回撥的方法,在等待時完全撤銷其執行緒
- 首先,如果你希望條件很快得到滿足(可能在幾微秒之內),則短暫的自旋可能會很有效,因為它避免了上下文切換的開銷和延遲.