Posted at
gumiDay 8

C#で高速なTCPサーバを実装するためのasync/await入門

More than 3 years have passed since last update.

C#でTCPサーバを実装するにはいくつか方法があります。

まずTCPサーバの実装方法毎の比較をし、その中でも記述量とパフォーマンスのバランスに優れたasync修飾子の使い方を紹介をします。


実装方法の比較1

実装方法
行数
難易度
秒間リクエスト数

async/await
101
易しい
56,916

非同期ソケット
170
難しい
68,272

ThreadPool
98
易しい
51,129

速度を求めるなら非同期ソケット、開発のし易さならasync修飾子という使い分けが良さそうな結果です。

という訳でasyncについて使い方などを見ていきます


async修飾子を使うと非同期処理が簡単

以下のコードは1秒間処理を遅延させるTask.Delay(1000)を合計100回実行していますが、処理は1秒足らずで終了し、計算結果も正しく表示されます。 2

従来でも ThreadPool.QueueUserWorkItem などを使えば時間のかかる処理をバックグラウンドに回すことは出来ましたが、この例の様に100個の非同期処理を「待ち合わせる」というのはここまで簡単には書けませんでした。

using System;

using System.Linq;
using System.Threading.Tasks;

class Program
{
public static void Main()
{
var tasks = Enumerable.Range(1, 100).Select(run).ToArray();
Task.WaitAll(tasks);
Console.WriteLine(tasks.Sum(x => x.Result));
}

static async Task<int> run(int n)
{
await timeConsumingWork().ConfigureAwait(false);
return n * 2;
}

static Task timeConsumingWork()
{
return Task.Delay(1000);
}
}


非同期ソケットがやっと扱いやすくなった

非同期ソケットは170行も実装に使っていますが、async/awaitは101行とコンパクトにまとまってます。

また、try/catchの範囲も非同期ソケットはコールバック関数を使っているため一括りに例外を補足できないのに対して、async/awaitの方はいつも通りの記述ができています。


という訳でasync/awaitオススメです

直感的に書けて読めるのでC#の非同期通信はasync修飾子をゴリゴリ使って書くのがよさそうです。パフォーマンスも悪くありませんし。





  1. ThreadPoolはThreadPool.SetMinThreads()でworkerスレッドを同時接続数と同等程度に設定しないと上記の性能が出ませんでした。また検証に使ったコードをgithubに置いています。 



  2. wandboxでコードを実行