2017-04-14 21 views
3

今、私は最初の場所でMarshal.GetExceptionCode()を使用するハックだけど、問題はそれについてではありません(Visual Studioのデバッガはまた、アクティブな例外を検出)例外非同期メソッドに巻き込まれた後

private static async Task TestAsync() 
{ 
    Log("TestAsync.Before"); 

    await HandleExceptionAsync(); 

    Log("TestAsync.After"); 
} 

private static async Task HandleExceptionAsync() 
{ 
    try 
    { 
     Log("HandleExceptionAsync.Try"); 
     await ThrowAsync(); 
    } 
    catch (InvalidOperationException) 
    { 
     Log("HandleExceptionAsync.Catch"); 
    } 

    Log("HandleExceptionAsync.AfterCatch"); 
} 

private static async Task ThrowAsync() 
{ 
    await Task.Delay(1000); 
    throw new InvalidOperationException("Delayed exception"); 
} 

private static void Log(string step) 
{ 
    Console.WriteLine($"{step}: {Marshal.GetExceptionCode()}"); 
} 

出力

TestAsync.Before: 0 
HandleExceptionAsync.Try: 0 
Exception thrown: 'System.InvalidOperationException' in Interactive.dll 
Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.ni.dll 
HandleExceptionAsync.Catch: -532462766 
HandleExceptionAsync.AfterCatch: -532462766 
TestAsync.After: -532462766 
The thread 9292 has exited with code 0 (0x0). 

例外は、それがキャッチされていてもかかわらのawaitチェーン全体でアクティブなままとなります。 私は、生成されたコードをチェックして、なぜこれが起こっていることが手掛かりを与えていない、(HandleExceptionAsyncステートマシンのMoveNextを生成)関連部分:

void IAsyncStateMachine.MoveNext() 
    { 
    int num1 = this.\u003C\u003E1__state; 
    try 
    { 
     if (num1 == 0) 
     ; 
     try 
     { 
     TaskAwaiter awaiter; 
     int num2; 
     if (num1 != 0) 
     { 
      Program.Log("HandleExceptionAsync.Try"); 
      awaiter = Program.ThrowAsync().GetAwaiter(); 
      if (!awaiter.IsCompleted) 
      { 
      this.\u003C\u003E1__state = num2 = 0; 
      this.\u003C\u003Eu__1 = awaiter; 
      Program.\u003CHandleExceptionAsync\u003Ed__1 stateMachine = this; 
      this.\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.\u003CHandleExceptionAsync\u003Ed__1>(ref awaiter, ref stateMachine); 
      return; 
      } 
     } 
     else 
     { 
      awaiter = this.\u003C\u003Eu__1; 
      this.\u003C\u003Eu__1 = new TaskAwaiter(); 
      this.\u003C\u003E1__state = num2 = -1; 
     } 
     awaiter.GetResult(); 
     awaiter = new TaskAwaiter(); 
     } 
     catch (InvalidOperationException ex) 
     { 
     Program.Log("HandleExceptionAsync.Catch"); 
     } 
     Program.Log("HandleExceptionAsync.AfterCatch"); 
    } 
    catch (Exception ex) 
    { 
     this.\u003C\u003E1__state = -2; 
     this.\u003C\u003Et__builder.SetException(ex); 
     return; 
    } 
    this.\u003C\u003E1__state = -2; 
    this.\u003C\u003Et__builder.SetResult(); 
    } 

私はこれを同期コンテキストに関連しているが表示されませんいずれか(この場合はコンソールアプリケーションなので、継続はプール上でスケジュールされます)、私の最高の推測では、いくつかのコールスタック操作が起こっていますが、私はこれに関する良い情報を見つけることができません。

誰もがこれが起こっている理由を説明し、これは、CLR /コンパイラ

UPD1に実装されている方法を説明するドキュメントへのリンクを提供することができれば、私は感謝し

:でアクティブな例外を示すVSデバッガのを追加しましたスクリーンショット非同期、同期には何も示さない

非同期 Repro inside the VS 2015 debugger

同期 No exception in sync version of the code

+0

'GetExceptionCode'には[構造化例外ハンドラのフィルタの外で動作が定義されていません](https://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v = vs.85))があります。 aspx)。捕捉された後で「アクティブ」例外が発生するシナリオがある場合は、問題の再現を投稿してください。 –

+0

@StephenClearyこの特定のコードは、VSデバッガ(例外変数)で例外をアクティブとして示しています。これはあなたのために書いてくれませんか? –

+0

私には、「アクティブ」とはそれがスローされていることを意味します(または、長期間使用された場合)。このような動作をしていますか、 '$ exception'デバッガ変数ですか? –

答えて

5

あなたはLog("HandleExceptionAsync.AfterCatch");にブレークポイントを置く場合は、コールスタックは、トリックを説明:

ConsoleApp1.exe!ConsoleApp1.Program.Log(string step) Line 107 C# 
ConsoleApp1.exe!ConsoleApp1.Program.HandleExceptionAsync() Line 95 C# 
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine) Unknown 
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown 
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown 
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() Unknown 
mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action action, bool allowInlining, ref System.Threading.Tasks.Task currentTask) Unknown 
mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations() Unknown 
mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree() Unknown 
mscorlib.dll!System.Threading.Tasks.Task.FinishStageTwo() Unknown 
mscorlib.dll!System.Threading.Tasks.Task.Finish(bool bUserDelegateExecuted) Unknown 
mscorlib.dll!System.Threading.Tasks.Task<System.Threading.Tasks.VoidTaskResult>.TrySetException(object exceptionObject) Unknown 
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Threading.Tasks.VoidTaskResult>.SetException(System.Exception exception) Unknown 
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception exception) Unknown 
ConsoleApp1.exe!ConsoleApp1.Program.ThrowAsync() Line 101 C# 
... (continues until the timer of Task.Delay) 

下のフレームを参照してください? HandleExceptionAsyncからログを記録していても、まだThrowAsyncに入っています。そんなことがあるものか? awaitキーワードを、あなたのHandleExceptionAsync方法は以下のようにチョップされるので、

mscorlib.dll!System.Threading.Tasks.Task<System.Threading.Tasks.VoidTaskResult>.TrySetException(object exceptionObject) Unknown 

が、それは簡単な置くために:答えはコールスタックでもあるもちろん

void HandleExceptionAsync1() 
{ 
    Log("HandleExceptionAsync.Try"); 
} 

void HandleExceptionAsync2() 
{ 
    Log("HandleExceptionAsync.AfterCatch"); 
} 

、これはより方法より複雑ですそれ。実際には、この方法は切り刻まれず、単純に状態機械に変換されます。しかし、このデモンストレーションでは、これは賢明に同等です。

HandleExceptionAsync2は、ThrowAsyncの後に実行する必要があります。したがって、HandleExceptionAsync2は連鎖として連鎖されます。ような何か:

ThrowAsync().ContinueWith(HandleExceptionAsync2); 

は(再び、これははるかに複雑なものを超えています。

継続が実際には同じコールスタックにインライン化され、実行されます

System.Threading.Tasks.Task<System.Threading.Tasks.VoidTaskResult>.TrySetException(object exceptionObject) 
(:ランタイムはThrowAsyncによって返されたタスクを完了したとき、私は単に説明)

「問題」は、のために簡素化しています上記のフレームを参照してください)。パフォーマンス上の理由から、これはTPLによってよく行われる最適化です。そのため、Log("HandleExceptionAsync.AfterCatch");に電話するときは、のキャッチブロックにはまだ実際にはThrowAsyncというキャッチブロックがあります。

+0

おかげさまで、非常に良い説明であり、今はすべてが今、完璧な意味を持ちます。私は生成された状態マシンをしばらく見ていましたが、コンパイラcatchによって生成された内部から実行されます。 –

関連する問題