任务取消和TaskContinuationOptions

时间:2013-03-25 00:25:33

标签: c# task-parallel-library

昨天我刚刚介绍了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?

1 个答案:

答案 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

希望这有帮助。