在Task運行過程中,我們可以通過.Net 4中的內置方法來取消Task的運行。 創建一個可取消的Task需要用到下麵的一些對象: 1.System.Threading.CancellationTokenSource實例 2.通過CancellationTokenSource.Token屬性獲得一個 ...
在Task運行過程中,我們可以通過.Net 4中的內置方法來取消Task的運行。
創建一個可取消的Task需要用到下麵的一些對象:
1.System.Threading.CancellationTokenSource實例
CancellationTokenSource tokenSource = new CancellationTokenSource();
2.通過CancellationTokenSource.Token屬性獲得一個取消令牌
CancellationToken token = tokenSource.Token;
3.創建Task對象,並且在構造函數傳入Action(或者Action<T>)委托作為第一個參數,CancellationToken作為第二個參數(重要)
Task task = new Task(() => { // do something }, token);
task.Start();
4.創建Task對象也可以通過調用System.Threading.Tasks.TaskFactory 類提供的靜態方法
Task task = Task.Factory.StartNew(() => { // do something ...... }, token);
如果想要取消Task的運行,除了要調用CancellationTokenSource實例的Cancel()方法之外,我們的Action委托中還需要檢測CancellationToken的取消狀態並編寫相應代碼(拋出異常)來阻止Task的運行。
可以通過以下方式來檢測Task取消狀態:
1.通過輪詢的方式檢測CancellationToken取消標記,該操作類似於輪詢非同步操作的IAsyncResult.IsCompleted狀態,也是通過在迴圈中判斷CancellationToken.IsCancellationRequested屬性來檢測Task是否被取消,如果為True則在Action委托中拋出異常來取消繼續運行Task。
static void Main(string[] args) { CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; Task task = new Task(() => { while (true) { if(token.IsCancellationRequested) { // 釋放資源操作等等... throw new OperationCanceledException(token); } Console.Write("."); Thread.Sleep(100); } }, token); Console.WriteLine("Task is Running."); Console.WriteLine("Press anykey to cancel task.");
task.Start();
Console.ReadKey(true); Console.WriteLine(); Console.WriteLine("Cancelling task."); tokenSource.Cancel();
Console.WriteLine("Main method complete."); Console.WriteLine("Press enter to finish."); Console.ReadLine(); }
如果不需要釋放系統資源,那麼可以直接調用CancellationToken.ThrowIfCancellationRequested()方法,其實現如下:
[__DynamicallyInvokable] public void ThrowIfCancellationRequested() { if (this.IsCancellationRequested) { this.ThrowOperationCanceledException(); } }
示例:
while (true) { token.ThrowIfCancellationRequested(); Console.Write("."); Thread.Sleep(100); }
2.通過委托(Delegate)來檢測Task是否取消,註冊一個在取消CancellationToken時調用的委托,當CancellationTokenSource發送取消請求時,該委托即會運行,我們可以在委托方法中實現通知功能等等。
static void Main(string[] args) { CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; Task task = new Task(() => { while (true) { token.ThrowIfCancellationRequested(); Console.Write("."); Thread.Sleep(100); } }, token); token.Register(() => { Console.WriteLine("The delegate is triggered."); }); Console.WriteLine("Task is Running."); Console.WriteLine("Press anykey to cancel task."); task.Start(); Console.ReadKey(true); Console.WriteLine(); Console.WriteLine("Cancelling task."); tokenSource.Cancel(); Console.WriteLine("Main method complete."); Console.WriteLine("Press enter to finish."); Console.ReadLine(); }
3.用WaitHandle來檢測Task是否取消,當在CancellationToken.WaitHandle上調用WaitOne()方法時,會阻止當前線程執行,直到該CancellationToken接收到取消請求標記時,被token阻止的Task線程才會釋放並繼續執行。
多個Task實例使用同一個CancellationToken時,當CancellationToken接收到取消請求標記時,所有在構造函數中使用該token實例化的Task都會被取消。WaitOne(int millisecondsTimeout)可以使用等待毫秒數作為參數,超過等待時間將會釋放阻止的線程。
不管WaitOne(int millisecondsTimeout)設置多長的等待時間,只要CancellationToken接收到取消請求標記時Task都會取消,而如果使用Thread.Sleep(100000)進行線程等待時,那麼即使CancellationToken接收到取消請求標記,該Task也會等到Thread.Sleep執行完成才會Cancel。
static void Main(string[] args) { CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; Task task = new Task(() => { while (true) { token.ThrowIfCancellationRequested(); Console.Write("."); Thread.Sleep(100); } }, token); Task task1 = new Task(() => { token.WaitHandle.WaitOne(); Console.WriteLine("WaitHandle released."); }, token); Console.WriteLine("Task is Running."); Console.WriteLine("Press anykey to cancel task."); task.Start(); task1.Start(); Console.ReadKey(true); Console.WriteLine(); Console.WriteLine("Cancelling task."); tokenSource.Cancel();
Console.WriteLine("Main method complete."); Console.WriteLine("Press enter to finish."); Console.ReadLine(); }
4.通過Task的IsCancelled屬性來判斷Task是否被取消,如果Task實例化時構造函數沒有傳入CancellationToken對象,則取消Task運行之後通過Task.IsCanceled屬性獲取到的值還是False而不是True。Task.ContinueWith()方法如果沒有傳入CancellationToken對象,則Task即使是取消執行也會繼續執行Task.ContinueWith()方法的Action委托,如果傳入與Task相同的CancellationToken對象,則Task取消執行後Task.ContinueWith()方法中的Action委托也不會繼續執行。
{ CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; Task task = new Task(() => { try { Console.WriteLine("Task: Running"); Thread.Sleep(5000); Console.WriteLine("Task: ThrowIfCancellationRequested"); token.ThrowIfCancellationRequested(); Thread.Sleep(2000); Console.WriteLine("Task: Completed"); } catch (Exception exception) { Console.WriteLine("Task: " + exception.GetType().Name); throw; } }, token); task.ContinueWith(t => Console.WriteLine("ContinueWith: tokenSource.IsCancellationRequested = {0}, task.IsCanceled = {1}, task.Exception = {2}", tokenSource.IsCancellationRequested, t.IsCanceled, t.Exception == null ? "null" : t.Exception.GetType().Name)); task.Start(); Thread.Sleep(1000); Console.WriteLine("Main: Cancel"); tokenSource.Cancel(); try { Console.WriteLine("Main: Wait"); task.Wait(); } catch (Exception exception) { Console.WriteLine("Main: Catch " + exception.GetType().Name); } Console.WriteLine("Main: task.IsCanceled = {0}", task.IsCanceled); Console.WriteLine("Press any key to exit..."); Console.ReadKey(true); }