1

我有一个长时间运行的操作,我正在使用 TPL 将其放在后台线程上。AggregateException我目前的工作是什么,但我对在取消请求期间应该在哪里处理我感到困惑。

在按钮单击事件中,我开始我的过程:

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...
    }
}

DuplicateSqlProcsFrom(CancellationToken _token, SqlConnection masterConn, string _strDatabaseA, string _strDatabaseB, bool _bCopyStoredProcs = true, bool _bCopyFuncs = true)我的方法中

DuplicateSqlProcsFrom(CancellationToken _token, SqlConnection masterConn, string _strDatabaseA, string _strDatabaseB, bool _bCopyStoredProcs = true, bool _bCopyFuncs = true)
{ 
    try
    {
        for (int i = 0; i < someSmallInt; i++)
        {
            for (int j = 0; j < someBigInt; j++)
            {
                // Some cool stuff...
            }

            if (_token.IsCancellationRequested)
                _token.ThrowIfCancellationRequested();
        }
    }
    catch (AggregateException aggEx)
    {
        if (aggEx.InnerException is OperationCanceledException)
            Utils.InfoMsg("Copy operation cancelled at users request.");
        return false;
    }
    catch (OperationCanceledException)
    {
        Utils.InfoMsg("Copy operation cancelled at users request.");
        return false;
    }
}

在按钮 Click 事件中(或使用delegate(buttonCancel.Click += delegate { / Cancel the Task / } ) I cancel theTask`,如下所示:

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.");
    }
}

这捕获了OperationCanceledException方法中的罚款并打印了我的消息,但在上面DuplicateSqlProcsFrom提供的回调中总是; 应该取消!asyncDupSqlProcs.ContinueWith(task => { ... });task.StatusRanToCompletion

Cancel()在这种情况下,捕获和处理任务的正确方法是什么。我知道在CodeProjectMSDN 上的示例中显示的简单案例中这是如何完成的,但是在运行延续时我在这种情况下感到困惑。

在这种情况下如何捕获取消任务以及如何确保task.Status正确处理?

4

2 回答 2

3

您在方法中捕获了OperationCanceledExceptionDuplicateSqlProcsFrom这会阻止它Task看到它并因此将其状态设置为Canceled. 因为处理了异常,所以在DuplicateSqlProcsFrom不抛出任何异常的情况下完成,并且其相应的任务在该RanToCompletion状态下完成。

DuplicateSqlProcsFrom不应该捕获OperationCanceledExceptionor AggregateException,除非它正在等待自己的子任务。抛出的任何异常(包括OperationCanceledException)都应该不被捕获以传播到延续任务。在您的延续switch声明中,您应该检查案例并处理适当的案例task.ExceptionFaultedCanceled

在您的延续 lambda 中,task.Exception将是一个AggregateException,它有一些方便的方法来确定错误的根本原因并处理它。检查MSDN 文档,特别是InnerExceptions(注意“S”)GetBaseExceptionFlattenHandle成员。


编辑:在得到 a TaskStatusofFaulted而不是Canceled.

在构建asyncDupSqlProcs任务的那一行,使用一个Task接受DuplicateSqlProcsFrom委托和CancellationToken. 这会将您的令牌与任务相关联。

当您调用ThrowIfCancellationRequested中的令牌时DuplicateSqlProcsFromOperationCanceledException抛出的 包含对已取消令牌的引用。当 Task 捕获异常时,它会将该引用与CancellationToken与之关联的引用进行比较。如果它们匹配,则任务转换为Canceled。如果他们不这样做,则任务基础结构已被编写为假定这是一个不可预见的错误,并且任务转换为Faulted

MSDN 中的任务取消

于 2012-03-13T13:49:28.860 回答
0

Sacha Barber 有很多关于 TPL 的系列文章。试试这个他用继续和取消来描述简单的任务

于 2012-03-13T11:41:08.477 回答