本文共 3194 字,大约阅读时间需要 10 分钟。
在编写异步代码时,CancellationToken是一个强大的工具,能够帮助我们在不影响已有任务执行的前提下,取消正在进行的操作。以下将详细探讨如何在不同场景中使用CancellationToken,以及如何正确处理取消请求。
CancellationToken提供了一种机制,允许我们在不影响主任务的前提下,取消某些阻塞式操作。以下是一个简单的示例:
public static int CancelableMethod(CancellationToken cancellationToken){ for (int i = 0; i != 100000; ++i) { Thread.Sleep(1); if (i % 1000 == 0) cancellationToken.ThrowIfCancellationRequested(); } return 42;} 在以上代码中,cancellationToken.ThrowIfCancellationRequested()会在每1000次迭代时检查取消请求。如果取消请求已被触发,会抛出TaskCanceledException,终止当前任务。
为了在特定时间后自动取消任务,可以使用CancellationTokenSource和CancelAfter方法。例如:
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"); }} 在这个示例中,cts.CancelAfter(TimeSpan.FromSeconds(2))会在2秒后自动取消任务。无论任务是否完成,sw.ElapsedMilliseconds都会记录总耗时。
在并行执行任务时,可以使用Parallel.ForEach并传递CancellationToken,以便在某些任务完成时立即取消所有相关任务:
public static void RotateMatrices(IEnumerablematrices, float degrees, CancellationToken token){ Parallel.ForEach(matrices, new ParallelOptions { CancellationToken = token }, matrix => matrix.Rotate(degrees));}
在这种情况下,如果主任务被取消,token会传播到所有正在执行的并行任务,导致它们提前终止。
在响应式编程中,CancellationToken可以用来控制流程。例如:
public static async Task RunGetWithTimeoutAsync(){ CancellationTokenSource source = new CancellationTokenSource(); await GetWithTimeoutAsync("http://www.baidu.com", source.Token);}public static async Task 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); }} 在这个示例中,CreateLinkedTokenSource创建了一个关联的取消 token源。CancelAfter方法在100毫秒后自动取消请求,避免长时间等待。
有时,我们需要将非标准化的取消机制与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 Task 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"); }} 在这个示例中,cancellationToken.Register注册了一个回调函数,用于在取消请求时调用ping.SendAsyncCancel()。确保在不再需要使用回调函数时,及时释放它以避免内存泄漏。
CancellationToken时,始终确保取消回调函数在不再需要时被释放,以避免内存泄漏。CancellationTokenSource中使用CancelAfter或CreateLinkedTokenSource是最简单且高效的方式来实现自动取消。CancellationToken,可能需要在必要时添加适配层。通过以上方法,可以灵活地控制异步任务的执行,并在需要时安全地终止不必要的操作。
转载地址:http://pbozz.baihongyu.com/