22
static async void Main(string[] args)
{
    Task t = new Task(() => { throw new Exception(); });

    try
    {                
        t.Start();
        t.Wait();                
    }
    catch (AggregateException e)
    {
        // When waiting on the task, an AggregateException is thrown.
    }

    try
    {                
        t.Start();
        await t;
    }
    catch (Exception e)
    {
        // When awating on the task, the exception itself is thrown.  
        // in this case a regular Exception.
    }           
}

TPL では、Task 内で例外をスローすると、AggregateException でラップされます。しかし、 awaitキーワード
を使用すると、同じことは起こりません。 その動作の説明は何ですか?

4

3 に答える 3

10

目標は、同期バージョンのように見える/動作させることです。Jon Skeetは、彼のEduasyncシリーズ、特にこの投稿でこれを説明する素晴らしい仕事をしています。

http://codeblog.jonskeet.uk/2011/06/22/eduasync-part-11-more-sophisticated-but-lossy-exception-handling/

于 2011-09-08T09:41:40.830 に答える
3

In TPLAggregateExceptionは、待機操作で複数のタスクを使用できるため (タスクには子タスクをアタッチできる)、それらの多くが例外をスローできるために使用されます。ここの子タスクセクションの例外を見てください:

https://msdn.microsoft.com/ru-ru/library/dd997417(v=vs.110).aspx

awaitあなたには常に1つのタスクしかありません。

https://msdn.microsoft.com/ru-ru/library/dd997415(v=vs.110).aspxも参照してください。

于 2017-03-23T10:28:00.100 に答える
0

Task.Wait() と await の例外タイプに違いがある理由について、Stephen Toub による詳細な説明を次に示します。

.NET 4.5 でのタスク例外処理

.NET 4 で Task.Wait を設計するとき、常に集計を伝達することを選択しました。その決定は、詳細を上書きしない必要性に影響されましたが、当時のタスクの主なユース ケースである、複数の例外の可能性が非常に一般的である fork/join 並列処理のユース ケースにも影響されました。

高レベルでは Task.Wait に似ていますが (つまり、タスクが完了するまで進行は行われません)、「待機タスク」は非常に異なる主要なシナリオ セットを表します。「待機タスク」の最も一般的な使用法は、fork/join の並列処理に使用されるのではなく、順次の同期コードを取得し、それを順次の非同期コードに変換することです。同期操作を実行するコード内の場所で、それをタスクで表される非同期操作に置き換えて「待機」します。そのため、確実に fork/join 操作 (Task.WhenAll の利用など) に await を使用できますが、80% のケースではありません。さらに、.NET 4.5 では、System.Runtime.ExceptionServices.ExceptionDispatchInfo が導入されています。これにより、スタック トレースや Watson バケットなどの例外の詳細を失うことなく、スレッド間で例外をマーシャリングできるという問題が解決されます。例外オブジェクトを指定すると、それを ExceptionDispatchInfo.Create に渡します。これは、Exception オブジェクトへの参照とその詳細のコピーを含む ExceptionDispatchInfo オブジェクトを返します。例外をスローするときは、ExceptionDispatchInfo の Throw メソッドを使用して、例外の内容を復元し、元の情報を失うことなく例外をスローします (現在の呼び出しスタック情報は、例外に既に格納されているものに追加されます)。これは、Exception オブジェクトへの参照とその詳細のコピーを含む ExceptionDispatchInfo オブジェクトを返します。例外をスローするときは、ExceptionDispatchInfo の Throw メソッドを使用して、例外の内容を復元し、元の情報を失うことなく例外をスローします (現在の呼び出しスタック情報は、例外に既に格納されているものに追加されます)。これは、Exception オブジェクトへの参照とその詳細のコピーを含む ExceptionDispatchInfo オブジェクトを返します。例外をスローするときは、ExceptionDispatchInfo の Throw メソッドを使用して、例外の内容を復元し、元の情報を失うことなく例外をスローします (現在の呼び出しスタック情報は、例外に既に格納されているものに追加されます)。

それを考慮して、常に最初のものをスローするか、常に集約をスローするかを選択できるので、「待機」については常に最初のものをスローすることを選択します。ただし、これは、同じ詳細にアクセスできないという意味ではありません。いずれの場合でも、Task の Exception プロパティは引き続きすべての例外を含む AggregateException を返すため、スローされたものをキャッチし、必要に応じて Task.Exception を参照するために戻ることができます。はい、これは「task.Wait()」と「await task」を切り替えるときの例外動作の不一致につながりますが、私たちはそれを 2 つの悪のうちの有意に小さいものと見なしました。

于 2019-07-31T15:10:02.887 に答える