void asyncメソッドを待つ


155

void asyncメソッドがそのジョブを完了するのをどのように待つことができますか?

たとえば、次のような関数があります。

async void LoadBlahBlah()
{
    await blah();
    ...
}

ここで、他の場所に進む前に、すべてがロードされていることを確認したいと思います。

回答:


234

ベストプラクティスは、関数async voidが発火してメソッドを忘れた場合にのみ関数をマークすることです。待機する場合は、としてマークする必要がありますasync Task

それでも待ちたい場合は、そのようにラップしてください await Task.Run(() => blah())


blahは明らかにタスクを返しており、非同期の署名があるのはなぜそれを待たずに呼び出すのでしょうか。VSでさえ警告を表示します。
batmaci

8
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
テーマフィールド

1
await Task.Run(() => blah())誤解を招く。これは、非同期関数の完了を待たずblah、タスクの(些細な)作成を待つだけで、blah()完了する直前に続行します。
Jonathan Lidbeck

@JonathanLidbeckステートメントは「何とか間違いの直前に続行します。「await Task.Run(()=> Thread.Sleep(10_000))」を試してください。タスクは次の行を実行する前に10秒以上待機します
Rohit Sharma

@RohitSharma、それはあなたの例では正しいですが、Thread.Sleep非同期ではありません。この質問は待っうとしているasync void機能を、言うasync void blah() { Task.Delay(10000); }
ジョナサンLidbeck


27

最善の解決策はを使用することasync Taskです。async voidいくつかの理由で回避する必要があります。その1つは構成可能性です。

メソッドを返すことができない場合Task(たとえば、それがイベントハンドラーである場合)は、を使用SemaphoreSlimして、終了しようとしているときにメソッドにシグナルを送ることができます。finallyブロックでこれを行うことを検討してください。


1

AutoResetEventを実行し、関数を呼び出してAutoResetEventを待機し、完了したら、それをasync void内に設定します。

void asyncから戻るタスクを待つこともできます


1

手動で何もする必要はありません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()リターン

あなたは本当にすることができない機能がそうすることはできませんawaitvoidLoadBlahBlah()void


LoadBlahBlah()終了するのではなく、待ちたいblah()
MBZ

10
残念ながら、async voidメソッドは(async Taskメソッドとは異なり)実行を一時停止しません
ghord

-1

私はこれが古い質問であることを知っていますが、これは私が歩き続ける問題ですが、非同期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を使用しないことだと思います。タスクがいつ完了したかを知る必要がある場合、それが非同期タスクの目的です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.