1. 程式人生 > >在C#中使用 CancellationToken 處理非同步任務

在C#中使用 CancellationToken 處理非同步任務

在 .NET Core 中使用非同步程式設計已經很普遍了, 你在專案中隨處可見 async 和 await,它簡化了非同步操作,允許開發人員,使用同步的方式編寫非同步程式碼,你會發現在大部分的非同步方法中,都提供了CancellationToken引數,本文主要介紹下 CancellationTokenSource 和 CancellationToken在非同步任務中的使用。 ![](https://blog-1259586045.cos.ap-shanghai.myqcloud.com/clipboard_20210316_072059.png) ## 手動取消任務 建立一個 CancellationTokenSource,然後呼叫非同步方法時,傳入 CancellationToken,它是一個輕量級物件,可以通知請求是否已取消,我們可以手動呼叫 cts.Cancel() 來取消任務,為了方面演示,這裡我有用到區域性方法。 ```csharp static async Task Main(string[] args) { async Task Execute(CancellationToken token) { await Task.Delay(3000, token); Console.WriteLine("Executed"); } CancellationTokenSource cts = new CancellationTokenSource(); _ = Execute(cts.Token); // 手動取消任務 cts.Cancel(); Console.ReadKey(); } ``` ## 定時取消任務 建立 CancellationTokenSource 的時候,可以傳入時間(毫秒或者Timespan), 通過它我們可以在等待一段時間後,自動取消任務。 ```csharp CancellationTokenSource cts = new CancellationTokenSource(1000); _ = Execute(cts.Token); Console.ReadKey(); ``` 我們也可以呼叫 cts.CancelAfter(1000), 它會在1s後取消任務。 ```csharp cts.CancelAfter(1000); ``` ## CancellationToken 註冊回撥 我們可以呼叫 Register()方法,註冊Token取消的回撥,引數需要傳入 Action 委託。 ```csharp CancellationTokenSource cts = new CancellationTokenSource(1000); cts.Token.Register(() => Console.WriteLine("任務已取消!")); // 開始非同步任務 _ = Execute(cts.Token); Console.ReadKey(); ``` Register() 註冊回撥後,返回一個 CancellationTokenRegistration 物件,同樣的,你可以在回撥函式執行前,移除註冊回撥,就像這樣: ```csharp cts.Token.Register(() => Console.WriteLine("任務已取消!")).Unregister(); ``` ## 在 HttpClient 中使用 同樣,你可以在 HttpClient 中使用傳入 CancellationToken (或者使用HttpClient的Timeout屬性),超時後,它會丟擲一個 TaskCanceledException 的異常: ```csharp CancellationTokenSource cts = new CancellationTokenSource(10); _ = await new HttpClient().GetAsync("https://www.youtube.com/",cts.Token); Console.ReadKey(); ``` ## 在 WebAPI中使用 我建立了一個 WebAPI 專案,其中的控制器程式碼如下,等待了5s,然後進行輸出資訊。 ```csharp [HttpGet] public async Task Index() { await Task.Delay(5000); Console.WriteLine("Executed"); return Ok(); } ``` 啟動專案後,我們在瀏覽器頁面上訪問介面,在第一次訪問介面等待響應時,我重新整理一次了頁面,現在程式的輸出資訊如下: ![](https://blog-1259586045.cos.ap-shanghai.myqcloud.com/clipboard_20210316_070908.png) 說明前臺頁面重新整理後,後臺並沒有做取消操作,執行了兩次! 我們可以把程式改成這樣,傳入 CancellationToken ```csharp [HttpGet] public async Task Index(CancellationToken token) { await Task.Delay(5000,token); Console.WriteLine("Executed"); return Ok(); } ``` 現在在瀏覽器訪問頁面,同樣的,第一次還未返回是,我們重新整理一次頁面,程式輸出如下: ![](https://blog-1259586045.cos.ap-shanghai.myqcloud.com/clipboard_20210316_071429.png) 只有一次輸出,第一次請求丟擲了一次 TaskCanceledException 異常,沒有繼續執行後邊的邏輯,當然你可以捕獲這個異常,返回更友好的提示! 歡迎掃碼關注我們的公眾號 【全球技術精選】,專注國外優秀部落格的翻譯和開源專案分享。