本文共 3591 字,大约阅读时间需要 11 分钟。
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"); }}
输出:
2004msUnhandled Exception: ... ...
只要执行代码时用到了超时,就该使用 CancellationTokenSource
和 CancelAfter
(或者用构造函数)。虽然还有其他途径可实现这个功能,但是使用现有的取消体系是最简单也是最高效的。
public class Matrix{ public void Rotate(float degrees) { }}//只做展示public static void RotateMatrices(IEnumerablematrices, float degrees, CancellationToken token){ Parallel.ForEach(matrices, new ParallelOptions { CancellationToken = token }, matrix => matrix.Rotate(degrees));}
注入取消请求
public static async Task RunGetWithTimeoutAsync(){ CancellationTokenSource source = new CancellationTokenSource(); await GetWithTimeoutAsync("http://www.baidu.com", source.Token);}public static async TaskGetWithTimeoutAsync(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); }}
输出:
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 ... ...
有一些外部的或以前遗留下来的代码采用了非标准的取消模式。现在要用标准的CancellationToken 来控制这些代码
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 TaskPingAsync(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"); }}
注意: 为了避免内存和资源的泄漏,一旦不再需要使用回调函数了,就要释放这个回调函数注册。
转载地址:http://pbozz.baihongyu.com/