C# 如何使用Continuation正确取消第三方物流任务

C# 如何使用Continuation正确取消第三方物流任务,c#,task-parallel-library,continuations,cancellation,request-cancelling,C#,Task Parallel Library,Continuations,Cancellation,Request Cancelling,我有一个长时间运行的操作,我正在使用TPL将其放在后台线程上。我目前所做的工作很有效,但我对在取消请求期间应该在哪里处理我的聚合异常感到困惑 在按钮单击事件中,我启动流程: private void button1_Click(object sender, EventArgs e) { Utils.ShowWaitCursor(); buttonCancel.Enabled = buttonCancel.Visible = true; try {

我有一个长时间运行的操作,我正在使用TPL将其放在后台线程上。我目前所做的工作很有效,但我对在取消请求期间应该在哪里处理我的
聚合异常
感到困惑

在按钮单击事件中,我启动流程:

private void button1_Click(object sender, EventArgs e)
{
    Utils.ShowWaitCursor();
    buttonCancel.Enabled = buttonCancel.Visible = true;
    try
    {
        // Thread cancellation.
        cancelSource = new CancellationTokenSource();
        token = cancelSource.Token;

        // Get the database names.
        string strDbA = textBox1.Text;
        string strDbB = textBox2.Text;

        // Start duplication on seperate thread.
        asyncDupSqlProcs =
            new Task<bool>(state =>
                UtilsDB.DuplicateSqlProcsFrom(token, mainForm.mainConnection, strDbA, strDbB),
                "Duplicating SQL Proceedures");
        asyncDupSqlProcs.Start();

        //TaskScheduler uiThread = TaskScheduler.FromCurrentSynchronizationContext();
        asyncDupSqlProcs.ContinueWith(task =>
            {
                switch (task.Status)
                {
                    // Handle any exceptions to prevent UnobservedTaskException.             
                    case TaskStatus.Faulted:
                        Utils.ShowDefaultCursor();
                        break;
                    case TaskStatus.RanToCompletion:
                        if (asyncDupSqlProcs.Result)
                        {
                            Utils.ShowDefaultCursor();
                            Utils.InfoMsg(String.Format(
                                "SQL stored procedures and functions successfully copied from '{0}' to '{1}'.",
                                strDbA, strDbB));
                        }
                        break;
                    case TaskStatus.Canceled:
                        Utils.ShowDefaultCursor();
                        Utils.InfoMsg("Copy cancelled at users request.");
                        break;
                    default:
                        Utils.ShowDefaultCursor();
                        break;
                }
            }, TaskScheduler.FromCurrentSynchronizationContext()); // Or uiThread.

        return;
    }
    catch (Exception)
    {
        // Do stuff...
    }
}
在按钮单击事件中(或使用
委托
(buttonCancel.Click+=委托{/Cancel the Task/}
),我按如下方式取消
任务`:

private void buttonCancel_Click(object sender, EventArgs e)
{
    try
    {
        cancelSource.Cancel();
        asyncDupSqlProcs.Wait();
    }
    catch (AggregateException aggEx)
    {
        if (aggEx.InnerException is OperationCanceledException)
            Utils.InfoMsg("Copy cancelled at users request.");
    }
}
这将捕获方法
DuplicateSqlProcsFrom
中的
OperationCanceledException
fine并打印我的消息,但是在
asyncDupSqlProcs.ContinueWith(task=>{…})提供的回调中;
任务上方的
状态始终是
RanToCompletion
,应该取消它

在本例中,捕获和处理
Cancel()
任务的正确方法是什么。我知道在中和中所示的简单案例中如何执行此操作,但在本例中,当运行延续时,我感到困惑


在这种情况下,我如何捕获取消任务,以及如何确保任务状态得到正确处理?

Sacha Barber有大量关于TPL的文章。试试看,他用continuation和canceling描述了简单的任务。您正在捕获
Duplicate SQLProcsFrom中的
OperationCanceledException
方法,该方法防止其
任务
看到它,并相应地将其状态设置为
已取消
。由于已处理异常,
DuplicateSqlProcsFrom
完成而不引发任何异常,其相应的任务在
RanToCompletion
状态下完成

DuplicateSqlProcsFrom
不应捕获
OperationCanceledException
AggregateException
,除非它正在等待自己的子任务。引发的任何异常(包括
OperationCanceledException
)应保持未捕获状态以传播到延续任务。在延续的
开关
语句中,应检查
故障
案例中的
任务。异常
,并在适当的案例中处理
取消

在后续lambda中,
任务.Exception
将是一个
聚合异常
,它有一些简便的方法来确定错误的根本原因,并对其进行处理。请特别检查
内部异常
(注意“S”),
GetBaseException
flant
Handle
成员


编辑:在获得
故障
任务状态
而不是
已取消时

在构造
asyncDupSqlProcs
任务的行上,使用一个
task
构造函数,该构造函数接受来自
delegate的
DuplicateSqlProcsf和将令牌与任务关联的
CancellationToken

当您在
DuplicateSqlProcsFrom
中的令牌上调用
ThrowIfCancellationRequested
时,抛出的
OperationCanceledException
包含对已取消令牌的引用。当任务捕获异常时,它会将该引用与与其关联的
CancellationToken
进行比较。如果它们匹配,然后任务转换到
已取消
。如果它们不匹配,则任务基础结构已编写为假设这是一个不可预见的错误,而任务转换到
故障


虽然这在理论上可以回答这个问题,但在这里包括答案的基本部分,并提供链接供参考。这是我提供的链接。虽然他涵盖了任务取消,但这不在我这里的更复杂的场景中-因此对我没有用。谢谢你的时间。谢谢你。这真的很有帮助ped.我已经到了从
DuplicateSqlProcsFrom
方法中删除Handling的阶段,但仍在尝试在其他地方捕获它。我已经按照您的建议做了,但在我的继续方法中,我只得到了
TaskStatus.Faulted
条件,从未
TaskStatus.Cancelled
。您知道为什么会这样吗?我想e在答案中添加了对这种行为的解释和解决方案。
private void buttonCancel_Click(object sender, EventArgs e)
{
    try
    {
        cancelSource.Cancel();
        asyncDupSqlProcs.Wait();
    }
    catch (AggregateException aggEx)
    {
        if (aggEx.InnerException is OperationCanceledException)
            Utils.InfoMsg("Copy cancelled at users request.");
    }
}