2017-08-09 313 views
0

我想在一定的毫秒后取消耗时的任务,对于我的情况,我认为CancellationToken.Register方法最适合与其他方法进行持续轮询或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(); 
} 

我,不过,里面cancellationToken.Register回调方法的执行过程中面临着异常处理的问题。对cancellationToken.ThrowIfCancellationRequested()的调用给我例外:“OperationCanceledException未被用户代码处理”,后面跟着“AggregateException was unhandled”。我已经阅读了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方法?

回答

0

您看到的错误完全是因为您没有在try-catch块内包装ThrowIfCancellationRequested。

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

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?