任务取消和TaskContinuationOptions

Bla*_*lau 4 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);
}
Run Code Online (Sandbox Code Playgroud)

我取消了以下任务:

private void CancelTasks()
{
    foreach (CancellationTokenSource tokenSource in MyTasks)
    {
        tokenSource.Cancel();                
    }
}
Run Code Online (Sandbox Code Playgroud)

我的工作人员功能如下:

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)
    {                
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

catch (OperationCanceledException)
{
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

编辑:

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

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

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

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

Moo*_*ght 10

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

延续可以查明Task前提任务的异常属性是否由前提引发异常.以下内容将a的结果打印NullReferenceException到控制台

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

如果task1抛出异常并且延迟没有捕获/查询此异常,则认为它未处理并且应用程序死亡.使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你不使用你的延续要么必须等待一个任务try/ catch块或查询任务的Result中try/ catch块

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);
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.