4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【C#】非同期処理の威力をさっくり体感する【async|await】

Last updated at Posted at 2024-07-14

はじめに

「非同期処理って体感的にわかるくらい変わるんスカ?」と先生に聞いたところ、以下のAPIに大量のリクエスト送ってみろと言われたのでやってみました。
UIを止めないとかはわかりやすいんですが、サーバー側では理論はわかっても体感がし辛いので記事にします。

環境

Mac
dotnet8

サンプルコード

以下のようなエンドポイントを作ります。
どちらも0.1秒待機して、ただ数字を返す簡単なAPIです。
違いは同期か非同期かだけ。

[ApiController]
[Route("api/[controller]")]
public class SampleController(ILogger<SampleController> logger) : ControllerBase
{
    [HttpGet("sync")]
    public int Index1()
    {
        logger.LogInformation("Index1 endpoint was called.");
        Thread.Sleep(100);
        return 1;
    }

    [HttpGet("async")]
    public async Task<int> Index2()
    {
        logger.LogInformation("Index2 endpoint was called.");
        await Task.Delay(100);
        return 1;
    }
}

jmeterでレスポンスタイムを計測

シナリオは省きますが、5000リクエストを4回それぞれぶん投げてみます。

スクリーンショット 2024-07-15 3.21.34.png

結果

api/sample/sync 同期

Label # Samples Average Min Max Std. Dev. Error % Throughput Received KB/sec Sent KB/sec Avg. Bytes
api/sync 20000 3926 0 18058 2461.24 0.190% 17.80322 2.85 2.27 163.6

api/sample/async 非同期

Label # Samples Average Min Max Std. Dev. Error % Throughput Received KB/sec Sent KB/sec Avg. Bytes
api/async 20000 100 100 140 2.1423981679412476 0.0 17.984447050190997 2.792506915019891 2.318307627563683 159.0

それぞれ見やすくすると以下のような感じになります。

API 平均時間 (ms) 最小時間 (ms) 最大時間 (ms)
api/sync 3926 0 18058
api/async 100 100 140

同期の方は非同期の40倍近く時間がかかっています。
非同期の方はほぼ理論値と言っていいでしょう。(同期の最小時間0秒はよくわからない)
はえーすっごい違う。

図解

例えばスレッドが3つ立ち上がった状態とします。

同期処理は以下のようにリクエストがあった時点でスレッドがブロックされるので、スレッド毎にキッチリ1ミリ秒のロックがかかります。

時刻 (ms) スレッド1 スレッド2 スレッド3
0 リクエスト1開始 リクエスト2開始 リクエスト3開始
1-1000 ブロック中 ブロック中 ブロック中
1000 リクエスト1完了 リクエスト2完了 リクエスト3完了
1001 リクエスト4開始 リクエスト5開始 リクエスト6開始

非同期処理なら以下のように待機中に別のタスクを挟めるので、ギチギチに処理が積まれていくイメージです。

時刻 (ms) スレッド1 スレッド2 スレッド3
0 リクエスト1開始 リクエスト2開始 リクエスト3開始
1-1000 リクエスト4~n開始 リクエスト5~n+1開始 リクエスト6~n+2開始
1000 リクエスト1完了 リクエスト2完了 リクエスト3完了
1001~n リクエスト4~n完了 リクエスト5~n+1完了 リクエスト6~n+2完了

まとめ

外部APIを叩く処理や、DBとの通信のような待機時間が発生する部分では非常に威力を発揮しそうです。
というかこの結果を見る限り、サーバーサイドでそれらを実装する上ではマストな要件な気がします。
なんでもかんでも非同期で実装すると痛い目を見ますが(実際に見た)IO処理が多発する実装をするときは、「とりあえず同期で実装する」のではなく、「とりあえず非同期で実装してみる」という頭の転換が必要そうです。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?