web-dev-qa-db-ja.com

非同期呼び出しを待たないことは依然として非同期ですよね?

これがばかげた質問(または重複)であるとすみません。

私は関数Aを持っています:

public async Task<int> A(/* some parameters */)
{
    var result = await SomeOtherFuncAsync(/* some other parameters */);

    return (result);
}

私には別の関数Bがあり、Aを呼び出しますが、戻り値は使用しません。

public Task B(/* some parameters */)
{
    var taskA = A(/* parameters */); // #1

    return (taskA);
}

Basyncとして宣言されておらず、Aの呼び出しを待機していないことに注意してください。 Aへの呼び出しは、Fire-and-Forget呼び出しではありません。Bは、次のようにCによって呼び出されます。

public async Task C()
{
    await B(/* parameters */);
}

#1にはawaitがないことに注意してください。私はこれがAへの呼び出しを同期的に行うと主張する同僚がいて、彼はConsole.WriteLineログを考え出し続けているようです。

B内の結果を待たないからといって、タスクチェーンisが待っていて、 A内のコードは、私たちがそれを待たないからといって変わることはありません。 Aからの戻り値は必要ないため、チェーン上の誰かがそれを待つ限り、呼び出しサイトでタスクを待つ必要はありませんCで発生します)。

私の同僚は非常に粘り強く、自分を疑い始めました。私の理解は間違っていますか?

17
xxbbcc

あなたが正しいです。タスクを作成することはそれだけを行い、いつ誰がその結果を待つかは気にしません。 await Task.Delay(veryBigNumber);SomeOtherFuncAsyncに入れてみてください。コンソールの出力は予想どおりです。

これはelidingと呼ばれます this blogpost を読むことをお勧めします。ここで、そのようなことをするべきか、すべきでない理由を確認できます。

また、コードをコピーして正しいことを証明する最小限の(少し複雑な)例:

class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine($"Start of main {Thread.CurrentThread.ManagedThreadId}");
            var task = First();
            Console.WriteLine($"Middle of main {Thread.CurrentThread.ManagedThreadId}");
            await task;
            Console.WriteLine($"End of main {Thread.CurrentThread.ManagedThreadId}");
        }

        static Task First()
        {
            return SecondAsync();
        }

        static async Task SecondAsync()
        {
            await ThirdAsync();
        }

        static async Task ThirdAsync()
        {
            Console.WriteLine($"Start of third {Thread.CurrentThread.ManagedThreadId}");
            await Task.Delay(1000);
            Console.WriteLine($"End of third {Thread.CurrentThread.ManagedThreadId}");
        }
    }

これはMiddle of mainEnd of third、実際には非同期であることを証明します。さらに、(ほとんどの場合)関数の終わりがプログラムの他の部分とは異なるスレッドで実行されていることがわかります。これらは実際には同期しているため(メインの開始、関数チェーンの呼び出し、3番目の戻り(awaitキーワードのある行で戻る可能性があります)、次にメインの始まりと中間の両方が常に同じスレッドで実行されます。 mainは、非同期関数が含まれていないかのように続行します。両方の関数のawaitキーワードの後の末尾は、ThreadPool(または使用している同期コンテキスト)のどのスレッドでも実行できます。

興味深いことに、Task.Delay in Thirdはそれほど長くはかからず、実際には同期的に終了しました。これはすべて1つのスレッドで実行されます。さらに、非同期で実行されますが、mightすべてが単一のスレッドで実行されます。非同期関数が複数のスレッドを使用することを示すルールはありません。I/ Oタスクが完了するのを待つ間、他の作業を実行するだけで十分です。

6
Ordoshsen