1. 程式人生 > 實用技巧 >c#非同步程式設計基礎

c#非同步程式設計基礎

執行緒(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程式的上下文中,阻塞可能會很麻煩
      • 所以,此類程式需要使用基於回撥的方法,在等待時完全撤銷其執行緒