使用CancellationToken.Register正确处理异常

时间:2017-08-09 16:45:08

标签: c# task

我试图在一定毫秒后取消一个耗时的任务,对于我的情况,我认为与其他使用常量轮询或WaitHandle的方法相比,CancellationToken.Register方法最合适。 CancellationToken.Register方法将帮助我定义一个委托,我计划将任务带到取消状态并停止进一步执行任务;任务被取消时将调用此委托(根据我的目标,稍后几毫秒)。这是我有的测试代码,我打算为以后的多个任务和嵌套任务进行扩展:

List<Task> tasks = new List<Task>();
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = tokenSource.Token;

Task t1 = Task.Factory.StartNew(() =>
{
    // check cancellation token before task begins
    if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested();

    // register a callback to handle cancellation token anytime it occurs
    cancellationToken.Register(() =>
    {
        Console.WriteLine("Task t1 cancelled");
        cancellationToken.ThrowIfCancellationRequested();
    });

    // simulating a massive task; note, it is not a repeating task
    Thread.Sleep(12000);
}, cancellationToken);

tasks.Add(t1);

try
{
    // cancel token after 3000 ms + wait for all tasks
    tokenSource.CancelAfter(3000);
    Task.WaitAll(tasks.ToArray());

    // OR wait for all tasks for 3000 ms and then cancel token immediately
    //Task.WaitAll(tasks.ToArray(), 3000);
    //tokenSource.Cancel();
}
catch (AggregateException e)
{
    Console.WriteLine("\nAggregateException thrown with the following inner exceptions:");
    // Display information about each exception. 
    foreach (var v in e.InnerExceptions)
    {
         if (v is TaskCanceledException)
              Console.WriteLine("   TaskCanceledException: Task {0}",                                              ((TaskCanceledException)v).Task.Id);
         else
              Console.WriteLine("   Exception: {0}", v.GetType().Name);
    }
    Console.WriteLine();
}
finally
{
    tokenSource.Dispose();
}

但是,我在CanceToken.Register中执行回调方法时遇到异常处理问题。对cancellationToken.ThrowIfCancellationRequested()的调用给了我异常“OperationCanceledException未被用户代码处理”,后跟“AggregateException未处理”。我已经阅读了关于VS设置的内容,取消了第一个OperationCanceledException异常的User-unhandled异常,但是我的应用程序在第二个AggregateException异常后终止; Task.WaitAll的try..catch块似乎无法解决这个问题。

我试图在一个try..catch块中包含cancellationToken.ThrowIfCancellationRequested(),但这种方法的问题是该任务继续执行我不想要的其余步骤。我没有看到轮询方法的这种行为。

// poll continuously to check for cancellation instead of Register
// but I do not want my massive task inside this repeating block
while (true)
{
    if (cancellationToken.IsCancellationRequested)
    {
        Console.WriteLine("Task t1 Canceled.");
        cancellationToken.ThrowIfCancellationRequested();
    }
}

我对CancellationToken.Register方法做错了什么?

1 个答案:

答案 0 :(得分:0)

您看到的错误正是因为您未将ThrowIfCancellationRequested包装在try-catch块中。

在我看来,这实际上取决于你正在做什么代替睡眠()。 以合作的方式结束任务,如

while(!cancellationToken.IsCancellationRequested)
{
    // Do stuff

    // Also check before doing something that may block or take a while
    if(!cancellationToken.IsCancellationRequested)
    {
        Stream.Read(buffer, 0, n);
    }
}

应该是阻止它的最好方法。 如果你真的需要阻止它,无论如何,我会把它包装在另一个任务中

Task.Run(() => 
{
    // Simulate a long running task
    Thread.Sleep(12*1000);
}, cancellationToken);

(测试和工作) 这样你就不会看到抛出任何异常未处理的异常。 另外,您可能需要查看此内容:How do I abort/cancel TPL Tasks?