4

昨日、タスク(TPL)を紹介されたばかりなので、タスクの使い方を理解するために、サンプルプロジェクトを少しやってみました。

私のサンプルプロジェクトは、プログレスバーのインクリメントを開始するスタートボタンでセットアップされています。タスクをキャンセルするための2番目のボタン。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.OnlyOnCanceledではなく、TaskContinuationOptions.OnlyOnRanToCompletionを使用した継続が実行されます。

明らかに私はこれを正しく行っていません。

編集

さらに読んでみると、次のようなコメントが見つかりました。

"catch(OperationCanceledException){}は、タスクのステータスをキャンセルではなくRanToCompletionとして設定します"

したがって、catch(OperationCanceledException){}を削除すると、タスクのステータスをキャンセルに設定できますが、プログラムはtokenSource.Token.ThrowIfCancellationRequested();で中断します。しかし、その後休憩を続けると、TaskContinuationOptions.OnlyOnCanceledを使用した継続タスクが実行されます。これは良いことです。

しかし、プログラムを中断せずに、タスクのステータスをキャンセルに設定したまま、tokenSource.Token.ThrowIfCancellationRequested()を呼び出すにはどうすればよいですか?

4

1 に答える 1

10

上記のコメントは、デバッガーとデバッガーの破損を防ぐために必要なオプションに関して正しいものです。ただし、以下は、継続を使用する方法と、実際にそれらの継続内のタスクからスローされた例外を処理する方法のより良い例を提供するはずです...

継続はTask、先行タスクの例外プロパティによって先行によって例外がスローされたかどうかを確認できます。以下は、の結果を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内のcatchタスクを照会する必要がありますResulttrycatch

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 に答える