4

昨天我刚刚被介绍给 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 的继续运行,而不是 TaskContinuationOptions.OnlyOnCanceled。

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

编辑

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

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

因此删除 catch (OperationCanceledException) {} 允许将任务的状态设置为已取消,但程序在 tokenSource.Token.ThrowIfCancellationRequested(); 但是如果我继续休息,带有 TaskContinuationOptions.OnlyOnCanceled 的继续任务运行,这很好。

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

4

1 回答 1

10

上面的评论在调试器和防止调试器中断所需的选项方面是正确的。但是,以下内容应该为您提供一个更好的示例,说明如何使用延续以及如何处理从这些延续中的任务引发的异常......

Task延续可以通过前面任务的异常属性来查明前面是否抛出了异常。以下将 a 的结果打印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/块中的任务catchResulttrycatch

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

希望这可以帮助。

于 2013-03-25T09:38:52.940 回答