本文大部分内容源于网络,https://hk.saowen.com/a/b4232bab0849c85ebd14db611a2a1cf5df69fdf2f32dcf61762acbe89bb065b8,并加入自己的理解。
使用TASK创建的任务能否取消,以及取消后会发生什么。
C#提供的任务取消的方式是“协作式”取消,在构造任务时,需要先构造一个System.Threading.CancellationTaskSource对象,该对象看起来像这样
public sealed class CancellationTaskSource:IDisposable{
public CancellationTokenSource();
public void Dispose(); // 释放资源 public bool IsCancellationRequested{ get ;}
public CancellationToken Token{ get ;}
public void Cancel(); // 内部调用Cancel并传递false
public void Cancel( bool throwOnFirstException);
}
CancellationTaskSource对象中含有一个Token,它的类型是CancellationToken,这是一个轻量级值类型,它包含一个私有的对CancellationTaskSource的引用,当构造Task时,需要将这个Token传入,如下所示
static void Main( string [] args){
CancelTask();
Console.WriteLine( " Async Run " );
Console.Read();
}
public static void CancelTask(){
// 定时1000ms后自动取消任务
var cancelSource = new CancellationTokenSource( 1000 );
Task.Run(() => {
// 模拟其他任务
Thread.Sleep( 4000 );
Console.WriteLine( " Task over " );
}, cancelSource.Token);
cancelSource.Token.Register(() => Console.WriteLine( " Task cancel " ));
}
运行后可以看到大约1秒后,控制台显示了Task cancel,并且在没有显示Task over。证明任务被取消了。Task.Run()方法的其中一个重载是接受一个CancelToken参数,我们传入的是cancelSource的Token,这表明cancelSource就可以控制Task的取消与否。我的做法是在1秒后自动取消任务,也可以手动调用cancelSource.Cancel()方法来显式取消任务。我在Token上订阅了取消的事件,在任务被取消后,执行我的方法,这里是控制台打印出Task cancel。也可以在任务中,利用“闭包”,将Token传入到任务中,如下
static void Main( string [] args){
CancelTask();
Console.WriteLine( " Async Run " );
Console.Read();
}
public static void CancelTask(){
var cancelSource = new CancellationTokenSource ( 1000 );
Task.Run(() => {
int count = 0 ;
for ( int i = 0 ; i <1000 ; i++ )
{
if (cancelSource.IsCancellationRequested)
break ;
count+= i;
Thread.Sleep( 30 );
}
Console.WriteLine(count);
}, cancelSource.Token);
cancelSource. Token.Register(() => Console.WriteLine( " Task cancel " ));
}
手动取消有2个方法,一是cancelSource.Cancel(),另一个是cancelSource.CancelAfter(),示例如下
public static void CancelTask(){
var cancelSource = new CancellationTokenSource();
Task.Run(() =>
{
Thread.Sleep( 4000 );
Console.WriteLine( " Task over " );
}, cancelSource.Token);
cancelSource.Token.Register(() => Console.WriteLine( " Task cancel " ));
// 此处调用cancelSource.Cancel()的话会立即结束任务
// 该方法会大约1秒后取消任务
cancelSource.CancelAfter( 1000 );
}
该示例和上面的在构造函数中传入1000ms的延时是一样的效果,若调用Cancel(),则立刻结束任务。
自动取消的方法在没有循环的情况下可以顺利完成,但在存在无线循环的情况下无法结束(但是Task cancel正常显示)。解决方法是在以上代码中再加入IsCancellationRequested判断,如下:
while (isLoop)
{
if (cancelTokenSource.IsCancellationRequested)
{
isLoop = false;
}
}
以上问题即可解决