取消任务
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
1. 取消请求
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;
}
2. 超时后取消
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");
}
}
输出:
2004ms
Unhandled Exception: ... ...
只要执行代码时用到了超时,就该使用 CancellationTokenSource
和 CancelAfter
(或者用构造函数)。虽然还有其他途径可实现这个功能,但是使用现有的取消体系是最简单也是最高效的。
3. 取消并行
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));
}
4. 取消响应式代码
注入取消请求
- 某一个层次的代码需要响应取消请求,同时它本身也要向下一层代码发出取消请求(但不会向上传递)。
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);
}
}
输出:
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 ... ...
5. 与其他取消体系的互操作
有一些外部的或以前遗留下来的代码采用了非标准的取消模式。现在要用标准的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<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");
}
}
注意: 为了避免内存和资源的泄漏,一旦不再需要使用回调函数了,就要释放这个回调函数注册。
如果您认为这篇文章还不错或者有所收获,您可以通过右边的"打赏"功能 打赏我一杯咖啡【物质支持】,也可以点击左下角的【好文要顶】按钮【精神支持】,因为这两种支持都是我继续写作,分享的最大动力!
作者: 大师兄石头
来源: https://bigbrotherstone.cnblogs.com/
声明: 原创博客请在转载时保留原文链接或者在文章开头加上本人博客地址,如发现错误,欢迎批评指正。凡是转载于本人的文章,不能设置打赏功能,如有特殊需求请与本人联系!