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结果。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论