>>返回《C# 併發編程》html
CancellationToken.None
是一個等同於默認的特殊值,表示這個方法是永遠不會被取消的。編程
實例代碼併發
static async Task CancelableMethodAsync(CancellationToken token) { await Task.Delay(1000, token); throw new ArgumentException(); } public static async Task IssueCancelRequestAsync() { var cts = new CancellationTokenSource(); var task = CancelableMethodAsync(cts.Token); // 這裏,操做在正常運行。 // 發出取消請求。 cts.Cancel(); //(異步地)等待操做結束。 try { await task; // 如運行到這裏,說明在取消請求生效前,操做正常完成 。 } catch (OperationCanceledException ex) { // 如運行到這裏,說明操做在完成前被取消。 System.Console.WriteLine(ex.GetType().Name); } catch (Exception ex) { // 如運行到這裏,說明在取消請求生效前,操做出錯並結束。 System.Console.WriteLine(ex.GetType().Name); } }
輸出:異步
TaskCanceledException
public static int CancelableMethod(CancellationToken cancellationToken) { for (int i = 0; i != 100000; ++i) { // cancellationToken.WaitHandle.WaitOne(1000); Thread.Sleep(1); // 這裏作一些計算工做。 if (i % 1000 == 0) cancellationToken.ThrowIfCancellationRequested(); } return 42; }
public static async Task IssueTimeoutAsync() { Stopwatch sw = Stopwatch.StartNew(); try { var cts = new CancellationTokenSource(); var token = cts.Token; cts.CancelAfter(TimeSpan.FromSeconds(2)); await Task.Delay(TimeSpan.FromSeconds(4), token); } finally { System.Console.WriteLine($"{sw.ElapsedMilliseconds}ms"); } }
輸出:async
2004ms Unhandled Exception: ... ...
只要執行代碼時用到了超時,就該使用 CancellationTokenSource
和 CancelAfter
(或者用構造函數)。雖然還有其餘途徑可實現這個功能,可是使用現有的取消體系是最簡單也是最高效的。函數
public class Matrix { public void Rotate(float degrees) { } } //只作展現 public static void RotateMatrices(IEnumerable<Matrix> matrices, float degrees, CancellationToken token) { Parallel.ForEach(matrices, new ParallelOptions { CancellationToken = token }, matrix => matrix.Rotate(degrees)); }
注入取消請求url
public static async Task RunGetWithTimeoutAsync() { CancellationTokenSource source = new CancellationTokenSource(); await GetWithTimeoutAsync("http://www.baidu.com", source.Token); } public static async Task<HttpResponseMessage> GetWithTimeoutAsync(string url, CancellationToken cancellationToken) { var client = new HttpClient(); using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { cts.CancelAfter(TimeSpan.FromMilliseconds(100)); var combinedToken = cts.Token; return await client.GetAsync(url, combinedToken); } }
輸出:pwa
Unhandled Exception: Unhandled exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.AggregateException: One or more errors occurred. (A task was canceled.) ---> System.Threading.Tasks.TaskCanceledException ... ...
code
有一些外部的或之前遺留下來的代碼採用了非標準的取消模式。如今要用標準的CancellationToken 來控制這些代碼htm
public static async Task RunPingAsync() { var cts = new CancellationTokenSource(); var task = PingAsync("192.168.0.101", cts.Token); //cts.Cancel(); await task; } public static async Task<PingReply> PingAsync(string hostNameOrAddress, CancellationToken cancellationToken) { Stopwatch sw = Stopwatch.StartNew(); try { var ping = new Ping(); using (cancellationToken.Register(() => ping.SendAsyncCancel())) { return await ping.SendPingAsync(hostNameOrAddress); } } finally { System.Console.WriteLine($"{sw.ElapsedMilliseconds}ms"); } }
注意: 爲了避免內存和資源的泄漏,一旦再也不須要使用回調函數了,就要釋放這個回調函數註冊。