1

タスク並列ライブラリや継続と合わせて、システムリソースを効率的に使用することに興味があります。

GetResponseAsync()別の最近の質問で定義された拡張メソッドを使用する次のシナリオを検討してください。

WebRequest request = HttpWebRequest.Create(uri);
Task<WebResponse> responseTask = request.GetResponseAsync(cancellationToken);
Func<Task<WebResponse>, WebResponse> continuation =
    task =>
    {
        WebRequest followup = HttpWebRequest.Create(uri2);
        return followup.GetResponseAsync(cancellationToken).Result;
    };
Task<WebResponse> finalResultTask = responseTask.ContinueWith(continuation, cancellationToken);

この構成には複数の問題があり、これをどのように処理するのが最善かを考えています。これまでに確認した主な項目は次のとおりです。

  1. のコアの実行はresponseTask、非同期実行中にユーザー スレッドをブロックしないことで、リソースを効率的に使用します。ただし、継続はFuncラムダとして定義されているため、継続を実行するスレッドreturnは、フォローアップ リクエストの実行が完了するまで、その行でブロックされます。より良い状況は、ユーザー スレッドをブロックせずに、継続のような動作を提供することです。

  2. キャンセルに関してresponseTaskとの動作は異なります。実行finalResultTask中に操作をキャンセルすると 状態になります。ただし、 の実行中に操作がキャンセルされた場合、プロパティにアクセスしようとすると例外が発生し、タスクが状態になります。responseTaskresponseTaskTaskStatus.CanceledfinalResultTaskResultTaskStatus.Failed

    • 動作は、障害に関しても異なる場合があります。継続から戻るときにプロパティにAggregateExceptionアクセスしようとしたときに がスローされた場合、二重にラップされた真の内部例外が発生する可能性があります (ここで特別なケースが発生するかどうかはわかりません)。最初のタスクで失敗した場合の実際の例外。ResultfinalResultTaskInnerExceptionresponseTask.Exception
4

1 に答える 1

3

Unwrap拡張メソッドを使用すると、両方の問題が解決されると思います。あなたの目標は、継続関数の結果として新しいタスクを返すことです。ただし、継続自体タスクとして実行されるため、追加レベルのネスト (タスクを返すタスク) が発生します。したがって、 を使用してこのネストを排除する必要がありUnwrapます。これにより、内部タスクの結果にバインドされたプロキシ タスクが得られます。

WebRequest request = HttpWebRequest.Create(uri);
Task<WebResponse> responseTask = request.GetResponseAsync(cancellationToken);
Func<Task<WebResponse>, Task<WebResponse> continuation =
    task =>
    {
        WebRequest followup = HttpWebRequest.Create(uri2);
        return followup.GetResponseAsync(cancellationToken);
    };

Task<Task<WebResponse>> finalResultTask = responseTask.ContinueWith(continuation);
Task<WebResponse> proxyTask = finalResultTask.Unwrap();

迅速な最適化: 継続関数は を介し​​て新しいタスクを生成する以外にほとんど何もしないGetResponseAsyncため、次のように指定することで実行オーバーヘッドを削減できますExecuteSynchronously

Task<Task<WebResponse>> finalResultTask = responseTask.ContinueWith(continuation, 
    TaskContinuationOptions.ExecuteSynchronously);

編集:Servyの提案に従ってCancellationToken、継続関数にもあなたを渡す必要があります:

Task<Task<WebResponse>> finalResultTask = responseTask.ContinueWith(continuation, 
    cancellationToken,
    TaskContinuationOptions.ExecuteSynchronously,
    TaskScheduler.Current);
于 2013-07-09T13:20:48.817 に答える