非同期メソッドが完了するのを待つ方法は?


138

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ループに再び入るように見えるので、それ自体が待っていますか?

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

回答:


131

避けてください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();
}

1
ありがとう、とても良いです。私は同様の問題で頭を悩ませていましたが、違いはあなたが言ったとおりに変更voidするTaskことでした。
ジェレミー

8
些細なことですが、規則に従うには、両方のメソッドに名前にAsyncを追加する必要があります。たとえば、
RequestToGetInputReportAsync

6
呼び出し元がMain関数の場合はどうなりますか?
シンビオント2016

14
@symbiont:次に使用GetAwaiter().GetResult()
Stephen Cleary

4
@AhmedSalah Taskはメソッドの実行を表します。つまり、return値が配置されTask.Result、例外が配置されTask.Exceptionます。を使用するvoidと、コンパイラには例外を配置する場所がないため、スレッドプールスレッドで例外が再発生します。
Stephen Cleary

229

知るべき最も重要なことasyncawaitそれがあるawait しない、完全に関連するコールを待ちます。どのようなawait行いは、直ちに同期操作の結果を返すことです操作がすでに完了している場合や、それがされていない場合は、残りの実行に継続スケジュールするasync方法をしてから、呼び出し元に制御を返すこと。非同期操作が完了すると、スケジュールされた完了が実行されます。

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

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;
}

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

「await」での回答は、コールの完了を待たないで、これらのキーワードのいくつかの、より詳細な説明があります。

一方、保留に関する@Stephen Clearyのガイダンスasync void。理由に関するその他の素晴らしい説明は、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


18
await「非同期待機」と考える(そして話す)ことは有用です。つまり、(必要であれば)メソッドをブロックしますが、スレッドはブロックしません。したがって、ブロッキング待機ではありませんが、RequestToSendOutputReport「待機」について話すことは理にかなっRequestToGetInputReportています。
スティーブンクリアリー2013年

@リチャードクック-追加説明ありがとうございました!
bmt22033 2013年

10
これは、実際の質問(つまり、非同期メソッドでスレッドごとにブロックする方法)をより明確に回答するため、受け入れられる答えになるはずです。
csvan 2015

最良の解決策は、タスクが完了するまで非同期で待機することですvar result = Task.Run(async()=> {return await yourMethod();})。Result;
Ramチッタラ2017年

70

タスクが完了するまでAsynMethodを待つ最適なソリューションは

var result = Task.Run(async() => await yourAsyncMethod()).Result;

15
または、これは非同期「void」の場合:Task.Run(async()=> {await yourAsyncMethod();})。Wait();
イジーHerník

1
yourAsyncMethod()。Resultに対するこれの利点は何ですか?
ジャスティンJスターク

1
.Resultプロパティにアクセスするだけでは、実際にはタスクの実行が完了するまで待機しません。実際、タスクが完了する前に呼び出されると、例外がスローされると思います。これをTask.Run()呼び出しでラップすることの利点は、Richard Cookが後述するように、「待機」は実際にはタスクが完了するのを待たないが、.Wait()呼び出しを使用するとスレッドプール全体がブロックされることです。これにより、別のスレッドで非同期メソッドを(同期的に)実行できます。少し混乱しますが、あります。
Lucas Leblanc 2018

私は必要なものだけが、結果での素敵な投げ、
ジェリー

assync()または待機のようなECMA7機能のクイックリマインダーは、ECMA7以前の環境では機能しません。
Mbotet

0

フラグを使用した回避策は次のとおりです。

//outside your event or method, but inside your class
private bool IsExecuted = false;

private async Task MethodA()
{

//Do Stuff Here

IsExecuted = true;
}

.
.
.

//Inside your event or method

{
await MethodA();

while (!isExecuted) Thread.Sleep(200); // <-------

await MethodB();
}

-1

タスクが完了するまで待つには、Wait()を置くだけです

GetInputReportViaInterruptTransfer().Wait();


これにより、現在のスレッドがブロックされます。したがって、これは通常行うのが悪いことです。
Pure.Krome

-4

実際、これはIAsyncActionを返す関数に役立つと思いました。

            var task = asyncFunction();
            while (task.Status == AsyncStatus.Completed) ;

-5

次のスニペットは、呼び出し元に戻る前に、待機中のメソッドが確実に完了するようにする方法を示しています。しかし、それが良い習慣だとは言いません。そうでないと思われる場合は、説明で私の回答を編集してください。

public async Task AnAsyncMethodThatCompletes()
{
    await SomeAsyncMethod();
    DoSomeMoreStuff();
    await Task.Factory.StartNew(() => { }); // <-- This line here, at the end
}

await AnAsyncMethodThatCompletes();
Console.WriteLine("AnAsyncMethodThatCompletes() completed.")

ダウンボーター、私が答えで尋ねたように、説明を気にしていますか?これは...私の知る限りではうまく動作しますので
Jerther

3
問題は、await+ を実行する唯一の方法は、2つの間の制御を放棄Console.WriteLineするになることTaskです。したがって、あなたの「解決策」は最終的にを生成しますがTask<T>、これは問題に対処していません。実行するとTask.Waitします(実際にデッドロックの可能性などを)処理を停止します。つまり、await実際には待機せず、2つの非同期実行可能部分を1つに結合するだけですTask(誰かが監視または待機できる)
Ruben Bartelink
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.