在进行异步开发或者使用Task类设计并行任务时,通常都会遇到在达成某些条件时终止任务,这时我们可以使用 取消令牌(CancellationToken)来协助我们完成这项工作。
令牌创建:
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
令牌使用(通过对应重载方法传入):
Task t = Task.Run(() => Task.Delay(5000, token), token);
await Task.Delay(1000, token);
取消任务:
//第一种方法
var timespan = new TimeSpan(0, 0, 0, 4);//设置超时时间
tokenSource.CancelAfter(timespan);
//第二种方法
tokenSource.Cancel();
CancelAfter方法还有整型(int)入参的重载方法,Cancel方法可以传入布尔类型(bool)参数指定是否立即弹出异常。
在一个任务中需要使用多个取消令牌(多种条件)的情况下可以使用CreateLinkedTokenSource方法。
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationTokenSource tokenSource1 = new CancellationTokenSource();
CancellationTokenSource cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token,tokenSource1.Token);
在任务取消后,我们可能需要做一下额外的工作,比如说通知任务已经取消,CancellationToken.Register方法用来处理取消后的回调操作。我们传入一个委托即可。
CancellationToken token = tokenSource.Token;
token.Register(() => Console.WriteLine("任务取消!这里是回调函数。"));
取消任务时我们可以在方法内通过token.IsCancellationRequested属性判断任务是否取消,也可以使用token.ThrowIfCancellationRequested()方法直接引发异常。
参考代码:
class CancelTesk
{
public async static Task CancelTeskMain()
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationTokenSource tokenSource1 = new CancellationTokenSource();
CancellationTokenSource cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token, tokenSource1.Token);
var timespan = new TimeSpan(0, 0, 0, 4);//设置超时时间
tokenSource.CancelAfter(timespan);
try
{
CancellationToken token = tokenSource.Token;
token.Register(() => Console.WriteLine("任务取消!这里是回调函数。"));
Console.WriteLine(DateTimeOffset.Now);
//输出token状态
Console.WriteLine($"输出token状态:{token.IsCancellationRequested}");
Task t = Task.Run(() => Task.Delay(5000, token), token);
Task t2 = Task.Run(() => TaskRun(token), token);
await Task.Delay(2000, token);
//输出任务是否取消
Console.WriteLine($"输出任务是否取消{t.IsCanceled},{t.Status}");
//取消任务
tokenSource.Cancel();
//await Task.WhenAll(t);
t2.Wait();
//输出token状态
Console.WriteLine($"输出token状态:{token.IsCancellationRequested}");
//输出任务是否取消
Console.WriteLine($"输出任务是否取消{t.IsCanceled},{t.Status}");
Console.WriteLine("结束任务");
}
catch (OperationCanceledException oex)
{
Console.WriteLine($"取消任务引发异常:{oex}");
}
catch (AggregateException aex)
{
Console.WriteLine($"取消任务引发异常:{aex}");
}
catch (Exception ex)
{
throw;
}
}
private static Task TaskRun(CancellationToken token)
{
try
{
//判断任务是否取消通过 token.IsCancellationRequested
while (!token.IsCancellationRequested)
{
//取消时直接引发异常
token.ThrowIfCancellationRequested();
Console.WriteLine("执行任务" + token.IsCancellationRequested);
if (token.IsCancellationRequested)
{
Console.WriteLine("跳出结束任务");
break;
}
Thread.Sleep(1000);
}
}
catch (Exception ex)
{
Console.WriteLine("任务执行异常:" + ex);
}
Console.WriteLine("退出循环,输出token状态:" + token.IsCancellationRequested);
return Task.CompletedTask;
}
}
输出结果: