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