[C#.NET][TPL] Task Receive Information for Cancel
用 CancellationTokenSource 類別 來中斷任務的執行,這會用到帶有 CancellationToken 結構 參數的 Taks 類別建構函式,
通知任務步驟:
- CancellationToken.IsCancellationRequested 屬性:判斷是否有調用 CancellationTokenSource.Cancel 方法。
- CancellationToken.ThrowIfCancellationRequested 方法:拋出一個 OperationCanceledException 例外,並改變 Task.IsCanceled 屬性。
- 利用 Task.ContinueWith 方法,調用子任務, 或是使用 CancellationToken.Register 方法,用來達到通知目的
利用 Task 的屬性觀察任務完成時的狀態
- IsCanceled:因取消而完成。
- IsCompleted:成功完成。
- IsFaulted:發生例外而完成。
更多的內容請參考:
http://msdn.microsoft.com/zh-tw/library/dd537607.aspx
內文章節:
判斷 CancellationToken.IsCancellationRequested 屬性:
開始實作:
private SynchronizationContext m_SynchronizationContext; private CancellationTokenSource m_cts; private ulong m_Counter = 0; public Form1() { InitializeComponent(); this.m_SynchronizationContext = SynchronizationContext.Current; }
判斷 CancellationToken.IsCancellationRequested 屬性:
利用 IsCancellationRequested 屬性,得知是否有調用 Cancel 方法,進而離開任務,不過這樣做,並沒有真正的改變 CLR 對 mainTask 的狀態,我們可以藉由以下的觀察看出結果
private void DoWork9() { var mainTask = new Task(() => { while (true) { this.m_Counter++; if (this.m_cts.Token.IsCancellationRequested) { break; } //update UI m_SynchronizationContext.Post(a => { this.label1.Text = this.m_Counter.ToString(); }, null); SpinWait.SpinUntil(() => false, 100); } }, this.m_cts.Token); mainTask.Start(); mainTask.ContinueWith(task => { var status = string.Format("任務完成,完成狀態為:\rIsCanceled={0}\rIsCompleted={1}\rIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); MessageBox.Show(status); }); }
當我調用 DoWork9 方法時,m_Counter 就會不斷的累加,並把結果呈現在 label1
private void button_Start_Click(object sender, EventArgs e) { this.m_cts = new CancellationTokenSource(); DoWork9(); }
當我調用 m_cts.Cancel 方法時,就會離開迴圈,並跳出 MessageBox,執行結果如下:
private void button_Stop_Click(object sender, EventArgs e) { this.m_cts.Cancel(); }
由下圖得知,CLR 並沒有改變 mainTask 的 IsCanceled 狀態
調用 CancellationToken.ThrowIfCancellationRequested 方法:
當調用 ThrowIfCancellationRequested 方法 時,可觀察出 CLR 改變了 mainTask 的 IsCanceled 屬性,CLR 不會把 ThrowIfCancellationRequested 方法當成是一個錯誤,而是協議式的取消,所以並沒有改變 IsFaulted 屬性。
PS.不能用除錯模式運行以下程式碼
private void DoWork10() { var mainTask = new Task(() => { while (true) { if (this.m_cts.Token.IsCancellationRequested) { this.m_cts.Token.ThrowIfCancellationRequested(); } this.m_Counter++; //update UI m_SynchronizationContext.Post(_ => { this.label1.Text = this.m_Counter.ToString(); }, null); SpinWait.SpinUntil(() => { if (this.m_cts.Token.IsCancellationRequested) { this.m_cts.Token.ThrowIfCancellationRequested(); } return false; }, 100); } }, this.m_cts.Token); mainTask.Start(); mainTask.ContinueWith(task => { try { var status = string.Format("任務完成,完成狀態為:\rIsCanceled={0}\rIsCompleted={1}\rIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); MessageBox.Show(status); } catch (AggregateException ex) { MessageBox.Show(ex.Message); } }); }
當我調用 DoWork10 方法時,m_Counter 就會不斷的累加,並把結果呈現在 label1
private void button_Start_Click(object sender, EventArgs e) { this.m_cts = new CancellationTokenSource(); DoWork10(); }
當我調用 m_cts.Cancel 方法時,就會離開迴圈,並跳出 MessageBox,執行結果如下:
private void button_Stop_Click(object sender, EventArgs e) { this.m_cts.Cancel(); }
由下圖得知,CLR 改變了 mainTask 的 IsCanceled 狀態了
若要更嚴僅,可在子任務加入 TaskContinuationOptions 列舉 參數;這表示,當 狀態條件 符合時,才會啟動子任務。
mainTask.ContinueWith(task => { try { var status = string.Format("任務完成,完成狀態為:\rIsCanceled={0}\rIsCompleted={1}\rIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); MessageBox.Show(status); } catch (Exception ex) { MessageBox.Show(ex.Message); } }, TaskContinuationOptions.OnlyOnCanceled);
調用 CancellationTokenRegistration 結構:
只要有註冊取消通知,一旦調用 CancellationTokenSource.Cancel 方法 後,就會執行註冊區塊。
this.m_cts.Token.Register(() => this.label1.Text = "任務取消");
private void DoWork11() { var mainTask = new Task(() => { while (true) { if (this.m_cts.Token.IsCancellationRequested) { this.m_cts.Token.ThrowIfCancellationRequested(); } this.m_Counter++; //update UI m_SynchronizationContext.Post(_ => { this.label1.Text = this.m_Counter.ToString(); }, null); SpinWait.SpinUntil(() => { if (this.m_cts.Token.IsCancellationRequested) { this.m_cts.Token.ThrowIfCancellationRequested(); } return false; }, 100); } }, this.m_cts.Token); mainTask.Start(); mainTask.ContinueWith(task => { try { //update UI this.m_cts.Token.Register(() => { m_SynchronizationContext.Post(a => { this.label1.Text = "任務取消"; }, null); }); var status = string.Format("任務完成,完成狀態為:\rIsCanceled={0}\rIsCompleted={1}\rIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); MessageBox.Show(status); } catch (Exception ex) { MessageBox.Show(ex.Message); } }, TaskContinuationOptions.OnlyOnCanceled); }
當我調用 m_cts.Cancel 方法時,就會離開迴圈,並跳出 MessageBox,執行結果如下:
private void button_Stop_Click(object sender, EventArgs e) { this.m_cts.Cancel(); }
以上出自:http://www.dotblogs.com.tw/yc421206/archive/2013/06/10/105434.aspx
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET