>>返回《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"); } }
注意: 为了避免内存和资源的泄漏,一旦再也不须要使用回调函数了,就要释放这个回调函数注册。