StephenClearyによるasyncawaitに関する記事からの抜粋:
図2AsyncVoidメソッドの例外をキャッチでキャッチできない
private async void ThrowExceptionAsync()
{
throw new InvalidOperationException();
}
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
try
{
ThrowExceptionAsync();
}
catch (Exception)
{
// The exception is never caught here!
throw;
}
}
... async voidメソッドからスローされた例外は、asyncvoidメソッドの開始時にアクティブだったSynchronizationContextで直接発生します...
それは実際にはどういう意味ですか?より多くの情報を収集するために、拡張例を作成しました。図2と同じ動作です。
static void Main()
{
AppDomain.CurrentDomain.UnhandledException += (sender, ex) =>
{
LogCurrentSynchronizationContext("AppDomain.CurrentDomain.UnhandledException");
LogException("AppDomain.CurrentDomain.UnhandledException", ex.ExceptionObject as Exception);
};
try
{
try
{
void ThrowExceptionVoid() => throw new Exception("ThrowExceptionVoid");
ThrowExceptionVoid();
}
catch (Exception ex)
{
LogException("AsyncMain - Catch - ThrowExceptionVoid", ex);
}
try
{
// CS1998 C# This async method lacks 'await' operators and will run synchronously.
async void ThrowExceptionAsyncVoid() => throw new Exception("ThrowExceptionAsyncVoid");
ThrowExceptionAsyncVoid();
}
// exception cannot be caught, despite the code running synchronously.
catch (Exception ex)
{
LogException("AsyncMain - Catch - ThrowExceptionAsyncVoid", ex);
}
}
catch (Exception ex)
{
LogException("Main", ex);
}
Console.ReadKey();
}
private static void LogCurrentSynchronizationContext(string prefix)
=> Debug.WriteLine($"{prefix} - " +
$"CurrentSynchronizationContext: {SynchronizationContext.Current?.GetType().Name} " +
$"- {SynchronizationContext.Current?.GetHashCode()}");
private static void LogException(string prefix, Exception ex)
=> Debug.WriteLine($"{prefix} - Exception - {ex.Message}");
デバッグ出力:
Exception thrown: 'System.Exception' in ConsoleApp3.dll
AsyncMain - Catch - ThrowExceptionVoid - Exception - ThrowExceptionVoid
Exception thrown: 'System.Exception' in ConsoleApp3.dll
An exception of type 'System.Exception' occurred in ConsoleApp3.dll but was not handled in user code
ThrowExceptionAsyncVoid
AppDomain.CurrentDomain.UnhandledException - CurrentSynchronizationContext: -
AppDomain.CurrentDomain.UnhandledException - Exception - ThrowExceptionAsyncVoid
The thread 0x1c70 has exited with code 0 (0x0).
An unhandled exception of type 'System.Exception' occurred in System.Private.CoreLib.ni.dll
ThrowExceptionAsyncVoid
The program '[18584] dotnet.exe' has exited with code 0 (0x0).
詳細が欲しい
async void
(なしawait
)との違いは何ですかvoid
CS1998 C# This async method lacks 'await' operators and will run synchronously.
await
で同期して実行される場合、なぜ単純なものとは異なる動作をするのvoid
ですか?async Task
ていないとawait
も異なる振る舞いTask
?async void
とはasync Task
。ここで提案さTask
れasync void
ているように、オブジェクトは実際に内部で作成されていますか?編集します。明確にするために、これはベストプラクティスに関する質問ではなく、コンパイラ/ランタイムの実装に関する質問です。
(私の例のように)現在の同期コンテキストがない場合、例外はどこで発生しますか?
慣例により、がの場合、それSynchronizationContext.Current
はnull
実際SynchronizationContext.Current
にはのインスタンスと同じですnew SynchronizationContext()
。つまり、「同期コンテキストなし」は「スレッドプール同期コンテキスト」と同じです。
したがって、表示されている動作は、async
ステートマシンが例外をキャッチし、スレッドプールスレッドで直接発生させていることcatch
です。このスレッドでは、例外をキャッチできません。
この動作は奇妙に思えますが、次のように考えてくださいasync void
。イベントハンドラーを対象としています。したがって、イベントを発生させるUIアプリケーションについて考えてみます。同期している場合、例外はUIメッセージ処理ループに伝播されます。このasync void
動作は、次のことを模倣することを目的としています。例外(後の例外を含むawait
)は、UIメッセージ処理ループで再発生します。これと同じロジックがスレッドプールコンテキストに適用されます。たとえば、同期System.Threading.Timer
コールバックハンドラーからの例外はスレッドプールで直接発生し、非同期System.Threading.Timer
コールバックハンドラーからの例外も発生します。
待機せずに同期して実行される場合、単にvoidとは異なる動作をするのはなぜですか?
async
ステートマシンは、特別な例外を処理しています。
待機のない非同期タスクもタスクとは異なる動作をしますか?
絶対に。async Task
非常によく似たステートマシンがあります-コードからの例外をキャッチし、返されたに配置しTask
ます。これは、elidingにおける落とし穴の1 async
/をawait
非自明なコードのために。
asyncvoidとasyncTaskのコンパイラの動作の違いは何ですか。
コンパイラーの場合、違いは例外の処理方法だけです。
これについて考える適切な方法は、それasync Task
が自然で適切な言語開発であるということです。async void
これは、C#/ VBチームが採用した奇妙なハックであり、下位互換性の大きな問題なしに非同期イベントを有効にします。F#、Python、JavaScriptなどの他のasync
/await
対応言語にはasync void
...の概念がないため、すべての落とし穴を回避できます。
ここで提案されているように、Taskオブジェクトは実際に非同期voidの内部で作成されていますか?
番号。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加