回答:
ベストプラクティスは、関数async void
が発火してメソッドを忘れた場合にのみ関数をマークすることです。待機する場合は、としてマークする必要がありますasync Task
。
それでも待ちたい場合は、そのようにラップしてください await Task.Run(() => blah())
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
await Task.Run(() => blah())
誤解を招く。これは、非同期関数の完了を待たずblah
、タスクの(些細な)作成を待つだけで、blah()
完了する直前に続行します。
Thread.Sleep
非同期ではありません。この質問は待っうとしているasync void
機能を、言うasync void blah() { Task.Delay(10000); }
最善の解決策はを使用することasync Task
です。async void
いくつかの理由で回避する必要があります。その1つは構成可能性です。
メソッドを返すことができない場合Task
(たとえば、それがイベントハンドラーである場合)は、を使用SemaphoreSlim
して、終了しようとしているときにメソッドにシグナルを送ることができます。finally
ブロックでこれを行うことを検討してください。
AutoResetEventを実行し、関数を呼び出してAutoResetEventを待機し、完了したら、それをasync void内に設定します。
void asyncから戻るタスクを待つこともできます
手動で何もする必要はありませんawait
。keywordは、blah()
戻るまで関数の実行を一時停止します。
private async void SomeFunction()
{
var x = await LoadBlahBlah(); <- Function is not paused
//rest of the code get's executed even if LoadBlahBlah() is still executing
}
private async Task<T> LoadBlahBlah()
{
await DoStuff(); <- function is paused
await DoMoreStuff();
}
T
オブジェクトのタイプであるblah()
リターン
あなたは本当にすることができない機能がそうすることはできませんawait
void
LoadBlahBlah()
void
LoadBlahBlah()
終了するのではなく、待ちたいblah()
私はこれが古い質問であることを知っていますが、これは私が歩き続ける問題ですが、非同期void署名メソッドでasync / awaitを使用するときにこれを正しく行う明確な解決策はまだありません。
しかし、私は.Wait()がvoidメソッド内で適切に機能していることに気付きました。
また、async voidとvoidは同じシグネチャを持っているため、次の操作が必要になる場合があります。
void LoadBlahBlah()
{
blah().Wait(); //this blocks
}
紛らわしいことに、async / awaitは次のコードをブロックしません。
async void LoadBlahBlah()
{
await blah(); //this does not block
}
あなたのコードを逆コンパイルすると、私の推測では、async voidは内部タスク(asyncタスクのように)を作成しますが、シグネチャはその内部タスクを返すことをサポートしていないためです
これは、内部的には非同期voidメソッドが内部的に非同期メソッドを「待機」できることを意味します。しかし、外部的には内部タスクがいつ完了したかを知ることができません。
したがって、私の結論は、async voidが意図したとおりに機能していることであり、内部タスクからのフィードバックが必要な場合は、代わりに非同期タスクシグネチャを使用する必要があります。
うまくいけば、私のとりとめのないことは、答えを探している人にとっても理にかなっています。
編集:実際に何が起こっているのかを確認するために、いくつかのサンプルコードを作成して逆コンパイルしました。
static async void Test()
{
await Task.Delay(5000);
}
static async Task TestAsync()
{
await Task.Delay(5000);
}
に変わります(編集:本体コードはここではなくステートマシンにあることを知っていますが、ステートマシンは基本的に同じでしたので、わざわざ追加していませんでした)
private static void Test()
{
<Test>d__1 stateMachine = new <Test>d__1();
stateMachine.<>t__builder = AsyncVoidMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncVoidMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
}
private static Task TestAsync()
{
<TestAsync>d__2 stateMachine = new <TestAsync>d__2();
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncTaskMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
AsyncVoidMethodBuilderもAsyncTaskMethodBuilderも、実際にはStartメソッドにコードがあり、ブロックするようにヒントを与えており、開始後は常に非同期で実行されます。
返ってくるTaskがないと、完了したかどうかをチェックする方法がありません。
予想どおり、非同期で実行されているタスクのみが開始され、コード内で続行されます。非同期タスク。最初にタスクを開始し、次にそれを返します。
したがって、私の答えは、非同期のvoidを使用しないことだと思います。タスクがいつ完了したかを知る必要がある場合、それが非同期タスクの目的です。