昨天我刚刚介绍了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<CancellationTokenSource> 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();
}
}
我的工作人员功能如下:
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)
{
}
}
在我读过的其他帖子中,有人建议使用tokenSource.Token.ThrowIfCancellationRequested()来设置TaskContinuationOptions.OnlyOnCanceled评估的条件。
我见过的所有例子都没有包括使用:
catch (OperationCanceledException)
{
}
但是,如果没有它,程序会在我调用tokenSource.Cancel()时停止;
按照目前的情况,当我调用tokenSource.Cancel()时,将运行使用TaskContinuationOptions.OnlyOnRanToCompletion的Continuation,而不是TaskContinuationOptions.OnlyOnCanceled。
显然我没有正确地做到这一点。
修改:
进一步阅读,我发现了一条评论:
“catch(OperationCanceledException){}会将任务的状态设置为RanToCompletion,而不是”已取消“
因此,删除catch(OperationCanceledException){}允许将任务的状态设置为取消,但程序在tokenSource.Token.ThrowIfCancellationRequested();但如果我继续休息,那么TaskContinuationOptions.OnlyOnCanceled的继续任务就会运行,这很好。
但是如何调用tokenSource.Token.ThrowIfCancellationRequested()而不允许程序中断并允许将任务状态设置为Canceled?
答案 0 :(得分:10)
上述注释在调试器和防止调试器中断所需的选项方面是正确的。但是,以下内容应该为您提供更好的示例,说明如何使用continuation以及如何处理这些continuation中的任务抛出的异常......
延续可以查明先行任务的异常属性是否由前提Task
抛出异常。以下内容将NullReferenceException
的结果打印到控制台
Task task1 = Task.Factory.StartNew (() => { throw null; });
Task task2 = task1.ContinueWith (ant => Console.Write(ant.Exception());
如果task1
抛出异常并且延迟未捕获/查询此异常,则认为未处理并且应用程序死亡。通过延续,只需通过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;
}
}
如果您不使用延续,则必须等待try
/ catch
块中的任务或查询Result
/ {中的任务try
{1}}阻止
catch
希望这有帮助。