昨天我刚接触到Tasks(TPL),于是我尝试做一个小样例项目来理解如何使用它们。
我的样例项目设置了一个开始按钮,该按钮开始递增一个进度条。第二个按钮用于取消任务。一个文本框用于报告使用TaskContinuationOptions.OnlyOnRanToCompletion进行继续时的情况,以及一个文本框用于报告使用TaskContinuationOptions.OnlyOnCanceled进行继续时的情况。
我可以创建和执行一个任务,但是以一种允许使用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而不是Canceled"
因此,移除catch(OperationCanceledException){}可以将任务的状态设置为已取消,但程序会在tokenSource.Token.ThrowIfCancellationRequested()处中断。如果我继续执行,则会运行带有TaskContinuationOptions.OnlyOnCanceled选项的连续任务,这很好。
但是,我如何调用tokenSource.Token.ThrowIfCancellationRequested(),使程序不会中断,并允许任务状态设置为已取消?
工具 > 选项 > 调试 > 然后取消选中 "启用仅限我的代码" 选项
。在此处找到此提示:如何:取消任务及其子级,在示例代码的最后评论中。 - Adrian Torrie