任务取消和TaskContinuationOptions

昨天我刚刚介绍了Tasks(TPL),所以我尝试做一些示例项目,以便了解如何使用它们。

我的示例项目设置了一个开始按钮,开始递增进度条。 第二个按钮取消任务。 用于报告何时调用使用TaskContinuationOptions.OnlyOnRanToCompletion的延续的文本框,以及用于报告何时调用使用TaskContinuationOptions.OnlyOnCanceled的继续的文本框。

我可以创建并执行一个Task,但是以一种让TaskContinuationOptions.OnlyOnCanceled标志继续触发的方式取消它是一个问题。

我按如下方式创建任务:

private void StartTask() { CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; Task task = null; task = Task.Factory.StartNew(() => DoWork(tokenSource), tokenSource.Token); //A list so that I can cancel the task when clicking a button on the UI Thread. MyTasks.Add(tokenSource); Task completed = task.ContinueWith(result => TaskCompleted(), TaskContinuationOptions.OnlyOnRanToCompletion); Task canceled = task.ContinueWith(result => TaskCanceled(), TaskContinuationOptions.OnlyOnCanceled); } 

我取消了以下任务:

 private void CancelTasks() { foreach (CancellationTokenSource tokenSource in MyTasks) { tokenSource.Cancel(); } } 

我的工作人员function如下:

 private void DoWork(CancellationTokenSource tokenSource) { if (progressBar1.InvokeRequired) { progressBar1.Invoke(new Action(() => DoWork(tokenSource))); return; } try { bool dowork = true; while (dowork) { tokenSource.Token.ThrowIfCancellationRequested(); if (progressBar1.Value == progressBar1.Maximum) { dowork = false; } Thread.Sleep(1000); progressBar1.PerformStep(); Application.DoEvents(); } countCompleted++; } catch (OperationCanceledException) { } } 

在我读过的其他post中,有人建议使用tokenSource.Token.ThrowIfCancellationRequested()来设置TaskContinuationOptions.OnlyOnCanceled评估的条件。

我见过的所有例子都没有包括使用:

 catch (OperationCanceledException) { } 

但是,如果没有它,程序会在我调用tokenSource.Cancel()时停止;

就目前而言,当我调用tokenSource.Cancel()时,运行TaskContinuationOptions.OnlyOnRanToCompletion的Continuation,而不是TaskContinuationOptions.OnlyOnCanceled。

显然,我没有正确地做到这一点。

编辑

进一步阅读,我发现一条评论说:

“catch(OperationCanceledException){}会将任务的状态设置为RanToCompletion,而不是”已取消“

因此,删除catch(OperationCanceledException){}允许将任务的状态设置为取消,但程序在tokenSource.Token.ThrowIfCancellationRequested(); 但如果我继续rest,那么TaskContinuationOptions.OnlyOnCanceled的延续任务就会运行,这很好。

但是如何调用tokenSource.Token.ThrowIfCancellationRequested()而不允许程序中断并允许将任务状态设置为Canceled?

上述注释在调试器和防止调试器中断所需的选项方面是正确的。 但是,以下内容应该为您提供更好的示例,说明如何使用continuation以及如何处理这些continuation中的任务抛出的exception…

延续可以查明先行任务的exception属性是否由先行任务抛出exception。 以下内容将NullReferenceException的结果打印到控制台

 Task task1 = Task.Factory.StartNew (() => { throw null; }); Task task2 = task1.ContinueWith (ant => Console.Write(ant.Exception()); 

如果task1抛出exception并且task1没有捕获/查询此exception,则认为它未处理并且应用程序死亡。 使用continuation,通过Status关键字建立任务结果就足够了

 asyncTask.ContinueWith(task => { // Check task status. switch (task.Status) { // Handle any exceptions to prevent UnobservedTaskException. case TaskStatus.RanToCompletion: if (asyncTask.Result) { // Do stuff... } break; case TaskStatus.Faulted: if (task.Exception != null) mainForm.progressRightLabelText = task.Exception.InnerException.Message; else mainForm.progressRightLabelText = "Operation failed!"; default: break; } } 

如果不使用continuation,则必须等待try / catch块中的任务或在try / catch块中查询任务的Result

 int x = 0; Task task = Task.Factory.StartNew (() => 7 / x); try { task.Wait(); // OR. int result = task.Result; } catch (AggregateException aggEx) { Console.WriteLine(aggEx.InnerException.Message); } 

希望这可以帮助。