開發者總嘗試對自己的代碼有更多的控制。“讓那個還在工作的線程馬上停止下來”就是諸多要求中的一種。然而事與願違,這里面至少存在兩個問題:
第一個問題是:正如線程不能立即啟動一樣,線程也並不能說停就停。無論采用何種方式通知工作線程需要停止,工作線程都會忙完手頭最緊要的活,然后在它覺得合適的時候退出。以最傳統的Thread.Abort方法為例,如果線程當前正在執行的是一段非托管代碼,那么CLR就不會拋出ThreadAbortException,只有當代碼繼續回到CLR中時,才會引發ThreadAbortException。當然,即便是在CLR環境中,ThreadAbortException也不會立即引發。
其次,正確停止線程,不在於調用者采取了什么行為(如最開始的Thread.Abort()方法),而更多依賴於工作線程是否能主動響應調用者的停止請求。大體機制是,如果線程需要被停止,那么線程自身就得負責開放給調用者這樣的接口:Cancled,然后線程在工作的同時,還得以某種頻率檢測Cancled標識,若檢測到Cancled,線程自己負責退出。
FCL現在為我們提供了標准的取消模式:協作式取消(Cooperative Cancellation)。協作式取消的機制就是上文提到的機制。下面是一個最基礎的協作式取消的樣例:
Thread t = new Thread(() =>
{
while ( true )
{
if (cts.Token.IsCancellationRequested)
{
Console.WriteLine( " 線程被終止! " );
break ;
}
Console.WriteLine(DateTime.Now.ToString());
Thread.Sleep( 1000 );
}
});
t.Start();
Console.ReadLine();
cts.Cancel();
調用者使用CancellationTokenSource的Cancle方法通知工作線程退出。工作線程則以大致1000毫秒的頻率一邊工作,一邊檢查是否有外界傳入進來的Cancel信號。若有這樣的信號,則負責退出。可以看到,在正確停止線程的機制中,真正起到主要作用的是線程本身。樣例中的工作代碼比較簡單,不過也足以說明問題。更復雜的計算式的工作,也應該以這樣的一種方式,妥善而正確地處理退出。
協作式取消中的關鍵類型是CancellationTokenSource。它有一個關鍵屬性Token,Token是一個名為CancellationToken的值類型。CancellationToken繼而進一步提供了布爾值的屬性IsCancellationRequested作為需要取消工作的標識。CancellationToken還有一個方法尤其值得注意,那就是Register方法。它負責傳遞一個Action委托,在線程停止的時候被回調,使用方法如:
{
Console.WriteLine( " 工作線程被終止了。 " );
});
本建議中的例子使用Thread進行了演示,使用ThreadPool也是一樣的模式,這里就不再贅述。后面我們還會講到任務Task,它依賴於CancellationTokenSource和CancellationToken完成了所有的取消控制。
微信掃一掃,關注最課程(www.zuikc.com),獲取更多我的文章,獲取軟件開發每日一練
之前的話題:
改善C#程序的建議6:在線程同步中使用信號量
改善C#程序的建議5:引用類型賦值為null與加速垃圾回收
改善C#程序的建議4:C#中標准Dispose模式的實現
改善C#程序的建議3:在C#中選擇正確的集合進行編碼
改善C#程序的建議2:C#中dynamic的正確用法
改善C#程序的建議1:非用ICloneable不可的理由
================================== 原文鏈接==================================