web-dev-qa-db-ja.com

非同期メソッドの完了を待つ方法

USB HIDクラスのデバイスにデータを転送するWinFormsアプリケーションを書いています。私のアプリケーションは、見つけることができる優れたGeneric HIDライブラリv6.0を使っています ここ 。一言で言えば、デバイスにデータを書き込む必要があるとき、これが呼び出されるコードです。

private async void RequestToSendOutputReport(List<byte[]> byteArrays)
{
    foreach (byte[] b in byteArrays)
    {
        while (condition)
        {
            // we'll typically execute this code many times until the condition is no longer met
            Task t = SendOutputReportViaInterruptTransfer();
            await t;
        }

        // read some data from device; we need to wait for this to return
        RequestToGetInputReport();
    }
}

私のコードがwhileループから抜けたとき、私はデバイスからいくつかのデータを読む必要があります。ただし、デバイスがすぐに応答できないため、続行する前にこの呼び出しが返されるのを待つ必要があります。現在存在するので、RequestToGetInputReport()は次のように宣言されています。

private async void RequestToGetInputReport()
{
    // lots of code prior to this
    int bytesRead = await GetInputReportViaInterruptTransfer();
}

その価値があるので、GetInputReportViaInterruptTransfer()の宣言は次のようになります。

internal async Task<int> GetInputReportViaInterruptTransfer()

残念ながら、私は.NET 4.5の新しいasync/awaitテクノロジの働きについてはあまりよく知らない。 awaitキーワードについて少し前に読みましたが、RequestToGetInputReport()内でGetInputReportViaInterruptTransfer()を呼び出すと待機するという印象がありましたが、RequestToGetInputReport()を呼び出すのとは違うようです。私はほとんどすぐにwhileループに再入ろうとしているのでそれ自体が待っている?

誰かが私が見ている行動を明確にすることができますか?

117
bmt22033

async voidを避けます。メソッドがTaskの代わりに void を返すようにします。それなら await それらにすることができます。

このような:

private async Task RequestToSendOutputReport(List<byte[]> byteArrays)
{
    foreach (byte[] b in byteArrays)
    {
        while (condition)
        {
            // we'll typically execute this code many times until the condition is no longer met
            Task t = SendOutputReportViaInterruptTransfer();
            await t;
        }

        // read some data from device; we need to wait for this to return
        await RequestToGetInputReport();
    }
}

private async Task RequestToGetInputReport()
{
    // lots of code prior to this
    int bytesRead = await GetInputReportViaInterruptTransfer();
}
111
Stephen Cleary

asyncawaitについて知っておくべき最も重要なことは、awaitdoes n'tが関連付けられた呼び出しの完了を待つことです。 awaitが行うことは、操作の結果を即座に同期的に返すことです操作が既に完了している場合、または、完了していない場合は、asyncメソッドの残りを実行する継続をスケジュールし、呼び出し元に制御を返します。非同期操作が完了すると、スケジュールされた完了が実行されます。

質問のタイトルにある特定の質問に対する答えは、適切なasyncメソッドを呼び出すことにより、Taskメソッドの戻り値(タイプWaitまたはTask<T>でなければなりません)をブロックすることです。

public static async Task<Foo> GetFooAsync()
{
    // Start asynchronous operation(s) and return associated task.
    ...
}

public static Foo CallGetFooAsyncAndWaitOnResult()
{
    var task = GetFooAsync();
    task.Wait(); // Blocks current thread until GetFooAsync task completes
                 // For pedagogical use only: in general, don't do this!
    var result = task.Result;
    return result;
}

このコードスニペットでは、CallGetFooAsyncAndWaitOnResultsynchronous非同期メソッドGetFooAsyncのラッパーです。ただし、このパターンは、非同期操作の実行中にスレッドプールスレッド全体をブロックするため、ほとんどの場合は使用しないでください。これは、APIによって公開されるさまざまな非同期メカニズムの非効率的な使用であり、APIを提供するために多大な努力を払っています。

"await"での答えは、call の完了を待たず、これらのキーワードのより詳細な説明がいくつかあります。

一方、async voidに関する@Stephen Clearyのガイダンスは保持されます。理由のその他の素敵な説明は、 http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/ で見つけることができます https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html

200
Richard Cook

タスクが完了するまでAsynMethodを待つための最良の解決策は

var result = Task.Run(async() => { return await yourAsyncMethod(); }).Result;
55
Ram chittala