しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET SignalR で非同期処理を行う

SignalR は基本的に非同期で通信が行われるので、クライアント側ではブロッキングせずに通信を行うことが出来ますが、サーバ側の実装となるとまた別の話になります。

ASP.NET MVC などと同じように Hub のメソッド内でファイル、ネットワーク I/O などの時間のかかる処理を同期的に行うと、当然ながら実行しているスレッドを止めてしまいます。

例として指定された URL のページタイトルを取得するメソッドを作成します。

[HubName("web")]
public class WebHub : Hub
{
    public string GetTitle(string url)
    {
        var client = new WebClient();

        var contents = client.DownloadString(url);

        var match = Regex.Match(contents, @"<title>(.*)<\/title>");

        return match.Success ? match.Groups[1].Value : "";
    }
}

この場合だと DownloadString メソッドがネットワークからのダウンロードが完了するまで処理を返さないので、サーバが混雑していたりして応答に時間がかかると無駄にスレッドを止めてしまう訳です。

という訳で、ネットワーク I/O は非同期で行うようにしましょう。

ASP.NET MVC での非同期コントローラは .NET 4.5 ではタスクベースの非同期に対応したので、async/await キーワードを使って簡単に対応できるようになりました。SignalR も全く同じでタスクベースの非同期に対応しているので、以下のようにすっきりと書けます。

[HubName("web")]
public class WebHub : Hub
{
    public async Task<string> GetTitle(string url)
    {
        var httpClient = new HttpClient();

        var contents = await httpClient.GetStringAsync(url);

        var match = Regex.Match(contents, @"<title>(.*)<\/title>");

        return match.Success ? match.Groups[1].Value : "";
    }
}

同期版と殆ど同じように書けました。HttpClient は .NET 4.5 で追加されたクラスで async/await を使うために最適化されているので、.NET 4.5 では WebClient よりも簡単に使えます。

ASP.NET MVC よりも SignalR を使う時の方が、非同期処理には注意する必要がありますね。