我试图在一定毫秒后取消一个耗时的任务,对于我的情况,我认为与其他使用常量轮询或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方法做错了什么?
答案 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?