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()この場合、タスクをキャプチャして処理する正しい方法は何ですか。CodeProject のこの例と MSDNの例に示されている単純なケースでこれがどのように行われるかは知っていますが、この場合、継続を実行するときに混乱します。

この場合、キャンセル タスクをキャプチャするにはどうすればよいですか? また、タスクtask.Statusが適切に処理されるようにするにはどうすればよいですか?

4

2 に答える 2

3

メソッドで をキャッチしているOperationCanceledExceptionため、それが表示されなくなり、それに応じてステータスが に設定されます。例外が処理されるため、例外をスローせずに終了し、対応するタスクはその状態で終了します。DuplicateSqlProcsFromTaskCanceledDuplicateSqlProcsFromRanToCompletion

DuplicateSqlProcsFrom独自のサブタスクを待機していない限り、OperationCanceledExceptionまたはのいずれかをキャッチしてはなりません。AggregateExceptionスローされた例外 ( を含むOperationCanceledException) は、継続タスクに伝播するためにキャッチされないままにしておく必要があります。継続のswitchステートメントでは、ケースをチェックtask.Exceptionインし、適切なケースでもFaulted処理する必要があります。Canceled

継続ラムダでtask.ExceptionAggregateException、エラーの根本原因を特定し、それを処理するための便利なメソッドがいくつかある になります。特に("S" に注意してください)、、およびメンバーについては、 MSDN ドキュメントを確認してください。InnerExceptionsGetBaseExceptionFlattenHandle


編集:TaskStatusFaulted代わりに の取得についてCanceled

asyncDupSqlProcsタスクを構築する行でTaskDuplicateSqlProcsFromデリゲートとCancellationToken. これにより、トークンがタスクに関連付けられます。

ThrowIfCancellationRequestedでトークンを呼び出すとDuplicateSqlProcsFromOperationCanceledExceptionスローされる には、キャンセルされたトークンへの参照が含まれます。CancellationTokenタスクが例外をキャッチすると、その参照とそれに関連付けられている参照が比較されます。一致する場合、タスクは に遷移しCanceledます。そうでない場合、タスク インフラストラクチャは、これが予期しないバグであると想定するように記述されており、代わりにタスクが に移行しFaultedます。

MSDN のタスクのキャンセル

于 2012-03-13T13:49:28.860 に答える
0

Sacha Barber は、TPL に関する優れた一連の記事を書いています。これを試してみてください彼は、継続とキャンセルを伴う単純なタスクについて説明しています

于 2012-03-13T11:41:08.477 に答える