c# Task任务的取消方式
Task任务的取消
c# 任务的取消,需要用到CancellationTokenSource类,CancellationToken结构体。
注意:CancellationTokenSource是class类型,而CancellationToken是struct结构体。
任务内部"监听"CancellationToken方法
任务的内部在合适的时候不停地调用CancellationToken的ThrowIfCancellationRequested()方法,这个函数会抛出一个叫做OperationCanceledException的异常,它的实现(微软开源代码)如下:
public void ThrowIfCancellationRequested() { if (IsCancellationRequested) ThrowOperationCanceledException(); }
下面的列子通过Task.Run产生了一个任务。Task任务的内部正式通过抛出OperationCanceledException异常达到被取消的目的。而任务的外部则是通过调用CancellationTokenSource实例的Cancel()方法来触发取消的动作的。
下面的例子是一个取消Task任务的例子
using System; using System.Threading; using System.Threading.Tasks; class Program { static async Task Main() { var tokenSource2 = new CancellationTokenSource(); CancellationToken ct = tokenSource2.Token; var tokenSource3 = new CancellationTokenSource(); var task = Task.Run(() => { // Were we already canceled? ct.ThrowIfCancellationRequested(); bool moreToDo = true; while (moreToDo) { // Poll on this property if you have to do // other cleanup before throwing. // Clean up here, then... if(ct.IsCancellationRequested) { ct.ThrowIfCancellationRequested(); } } },tokenSource2.Token); // Pass same token to Task.Run. Thread.Sleep(5000); tokenSource2.Cancel(); // Just continue on this thread, or await with try-catch: try { await task; } catch (OperationCanceledException e) { Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}"); bool eqs = e.CancellationToken.Equals(tokenSource3.Token); System.Console.WriteLine($"Equals' result: {eqs}."); System.Console.WriteLine($"Task's status: {task.Status}"); } catch (Exception ex) { System.Console.WriteLine(ex.Message); } finally { tokenSource2.Dispose(); } Console.ReadKey(); } } /* The operation was canceled. OperationCanceledException thrown with message: The operation was canceled. Equals' result: False. Task's status: Canceled */
这个例子中我们在Task的外部创建了2个CancellationTokenSource实例,因为CancellationTokenSource类包含一个CancellationToken类的属性Token,这也就意味着例子当中包含了两个CancellationToken实例。
ThrowIfCancellationRequested()方法会把调用它的CancellationToken实例也作为参数一起携带抛出。
因此catch块参数e中包含的是tokenSource2.Token,因此它与tokenSource3.Token进行相等比较输出一定是false。
又因为Task.Run()方法中传入的第二个参数也是tokenSource2.Token,Task核心框架内部会比较OperationCanceledException异常中携带的CancellationToken实例和Task.Run()方法中传入的CancellationToken实例,如果两者是一样的,则Task的status状态设为Canceled,表示成功取消;如果两者不一样,Task任务仍然会退出,但是Task的status状态设为Faulted,表示出错。
如果将第29行改为:
},tokenSource3.Token); // Pass same token to Task.Run.
则,结果输出如下:
OperationCanceledException thrown with message: The operation was canceled.
Equals' result: False.
Task's status: Faulted
看起来,Task任务也被取消了,但是实际上是由于出错而退出。因此,Task.Run的第二个参数可以用来确保内部实际引发取消异常的CancellationToken实例和Task.Run传入的CancellationToken实例要一致才能成功取消,否则框架会误认为你是误操作而导致退出的。因此,Task.Run的CancellationToken参数是为了更加安全。
如果将29行改为不带CancellationToken参数的重载函数,那么返回的也是一样的Faulted结果。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
C#实现微信公众号群发消息(解决一天只能发一次的限制)实例分享
经过几天研究网上的代码和谢灿大神的帮忙,今天终于用C#实现了微信公众号群发消息,现在分享一下2013-09-09
最新评论