6

開始後に頻繁にキャンセルする必要がある短い非同期タスクがあります。「タスク」クラスには IsCanceled インジケーターがあり、非同期タスクが実行を完了せずにキャンセルされたことを示すのに便利だと思いますが、非同期タスクをキャンセル済みとしてマークする唯一の方法は非同期関数で TaskCanceledException をスローします。例外なく発生する状況を示すために、日常的に例外をスローすることは、例外を使用する必要があると私が理解している方法に反します。非同期タスクが頻繁に発生すると予想される場合に、非同期タスクをキャンセルすることを示すより良い方法を知っている人はいますか?

私の次善の策は、独自の IsCanceled プロパティを持つ構造体を返すことです。

(ここでは簡潔にするために、いくつかの優れたコーディングとスタイルの実践を無視しています)

class MightBeCanceled<T>
{
    public readonly T Value;
    public readonly bool IsCanceled;

    public MightBeCanceled(T value) { Value = value; IsCanceled = false; }
    public static MightBeCanceled<T> Canceled = new MightBeCanceled<T>(default(T), true);
    private MightBeCanceled(T value, bool isCanceled) { Value = value; IsCanceled = isCanceled; }
}

...

static async Task<MightBeCanceled<int>> Foo()
{
    if (someCancellationCondition)
        return MightBeCanceled<int>.Canceled;
    else
        return new MightBeCanceled<int>(42);
}

static async void Bar() 
{
    var mightBeCanceled = await Foo();

    if (mightBeCanceled.IsCanceled)
        ; // Take canceled action
    else
        ; // Take normal action
}

しかし、これは冗長で使いにくいようです。IsCanceled が 2 つ (Task に 1 つ、MightBeCanceled に 1 つ) あるため、一貫性の問題が発生することは言うまでもありません。

4

2 に答える 2

2

通常、キャンセルの目的は、非同期アクションの呼び出し元が、処理を停止する必要があることをアクションに通知することです。このためCancellationTokenに、非同期メソッドにを渡します。そのため、IsCancelledを設定するタスクを待機すると自分自身がスローされます。これは、外部から引き起こされた行動に対する例外的なシグナルを意味します。タスクのキャンセルは制御フローには使用しないでください。ただし、待機中のパーティが結果を必要としないことを通知した場合にのみ、非同期アクションを早期に終了するオプションを提供します。

非同期アクションが内部でキャンセルされた場合、すでに概念がオーバーロードされています。キャンセルの違いを反映することは価値があり、提案した方法と同様に結果コンテナーのプロパティである必要があります。しかし、キャンセルの概念の違いを反映するために、それを呼び出す代わりにMightBeCancelled<T>、おそらくのようなものです。InternallyCancellableResult<T>

于 2011-08-22T15:31:49.000 に答える
2

問題は、待機することを選択するまで、例外が監視されないことです。したがって、呼び出しの行為はawait、最終的な値が返されたとき、またはタスクが完了するのを待っているときに、最終的に例外を観察することを意味します。ただし、気にしない場合 (これはファイア アンド フォーゲット タスクです)、例外は問題になりません (ほとんどの場合)。

私も、これは少し奇妙で、集計例外を処理するために常にタスクを試行/キャッチする必要があります。ただし、少し考えてみてください。

 try
 {
     var myResult = await Foo();

     // Do Success Actions Here... 
 }
 catch(AggregateException e)
 {
     e.Flatten().Handle(ex =>
       {
           if(ex is OperationCanceledException)
           {
               // Do Canceled Thing Here
               return true;
           }

           return false;
       });
 }

それほど遠くありません。多くの点で、別のタスクをキャンセルすることを考えていますが、どうすればよいでしょうか? ThreadAbortException? キャンセル時に特定の例外を単にスローすることは、それほど難しいことではないようです。

これは、キャンセルの処理方法について複数の場所で提唱されているパターンとほぼ同じです。

于 2011-08-11T21:19:38.833 に答える