ASP.NET WebForms 4.5, WCF 4.5 における非同期 (async) メソッド


環境 :
Visual Studio 2012 Beta

こんにちは。
.NET ラボ で、セッションを聞いていただいた皆様、ありがとうございました。
「Q & A は懇親会で」と思っていたんですが、子供が夕方に高熱を出して、急遽、懇親会の参加ができなくなってしまいました。すみませんでした。

セッションの最後で説明した Web Form と WCF の async メソッドについて、説明が駆け足になりすぎて手順が説明できなかったので、本ブログにて補足しておきます。(主要な話は、「ASP.NET で非同期を乗りこなす」 のほうに記載してますので、ゆっくりとお試しください。)

ASP.NET MVC、ASP.NET Web API だけでなく、Web Forms、WCF などについても、下記の通り、非同期に対応しています。

 

ASP.NET Web Forms 4.5 の async メソッド

ASP.NET Web Form でも、Thread Pool を有効活用する async メソッドが構築できますが、説明した通り、ちょっと特殊な書き方になります。

まず、Page ディレクティブに、下記 太字を追加しておきます。

<%@ Page Title="Home Page" ... Async="true" %>

さらに、Web.config に、以下を記述します。

<configuration>
  <appSettings>
    . . .

    <add
      key="aspnet:UseTaskFriendlySynchronizationContext"
      value="true" />
  </appSettings>
  . . .
</configuration>

説明した通り、async キーワードを付けて、Page_Load や、Button1_Click などを実装したくなりますが、現 Beta 段階では、こうしたプログラミングは不可能です。従来通り、.NET 4 でサポートされていた Page.RegisterAsyncTask メソッドを使用して、非同期メソッドを登録する必要があります。

ただし、.NET 4.5 では、PageAsyncTask コンストラクタとして、PageAsyncTask(Func<Task> handler) が使えるようになっています。このため、以下の通り、async / await を使ってスッキリと記述できます。

protected void Page_Load(object sender, EventArgs e)
{
  Page.RegisterAsyncTask(new PageAsyncTask(async () =>
    {
      HttpClient cl = new HttpClient();
      HttpResponseMessage res = await cl.GetAsync(@"http://testsite/HeavyPage/");
      Label1.Text = res.ToString();
    }));
}

 

WCF 4.5 の async メソッド

こちら、実は、Parallel Programming Team のブログ に簡単に書かれているのですが (MSDN には、まだ載っていないようです)、.NET Framework 4.5 の WCF では Task を返すことが可能になっています。
このため、ASP.NET MVC などと同じく、非同期 (async) を使用した Thread Pool の有効活用が容易になっています。(ちなみに、WCF では、Worker Thread ではなく、Thread Pool の IO Thread が使用されます。)

下記のコードは、.NET 4 の WCF で可能だった非同期パターン (AsyncPattern = true) のプログラミング スタイルではありません。以前 述べたように、Task を使った同期に近いプログラミング スタイルで、非同期処理によって、スレッドの占有を無くす効率的なプログラム コードになっています。

[ServiceContract]
public interface IService1
{
    [OperationContract]
    Task<string> GetDataAsync();
}

public class Service1 : IService1
{
    public async Task<string> GetDataAsync()
    {
        HttpResponseMessage res = await GetHeavyPage();
        return res.ToString();
    }

    private Task<HttpResponseMessage> GetHeavyPage()
    {
        HttpClient cl = new HttpClient();
        return cl.GetAsync(@"http://localhost/HeavyWeb/");
    }
}

上記のメソッドの名称 (GetDataAsync) に注意してください。
WCF 4.5 では、「WCF 4.5 新機能」 で述べたように、クライアント アプリケーションで [サービス参照の追加] (SvcUtil.exe) をおこなうと、非同期用のメソッド (...Async という名前のオペレーション) が構築されます。例えば、「GetData」という名前のオペレーションがサーバーで提供されている場合、クライアント側では GetData と GetDataAsync という 2 つのオペレーションが生成されます。
しかし、上記のサービスを参照追加した場合、クライアント側で「GetDataAsyncAsync」が生成されるのではなく、「GetData」と「GetDataAsync」という 2 つのオペレーションが提供されます。注意していただきたいのは、これら (GetData と GetDataAsync) は、いずれも、クライアント側における同期 / 非同期パターンのオペレーションであり、いずれのオペレーションを使用しても、サーバー側では、スレッドの無駄使いをしない効率的な非同期動作となります。(サーバー側とクライアント側を混同しないように注意してください。)

なお、WCF Data Services は、ご存じの通り、WCF の REST (WebHttp) を使って実装されています。この WCF Data Services で動作を確認したところ、残念ながら、検索している間、Thread Pool の Thread (IO Thread) がずっと握られていました。 (.NET 4.5 Beta で確認。) 少なくとも Beta 段階では、WCF Data Services で、この非同期の仕組みは使われていないようです。(このため、WCF Data Services で、あまり時間のかかるクエリーは実行しないようにしましょう。スレッドがどんどん溜まっていく可能性があります。)

 

ということで、.NET Framework 4.5 における サーバー側の 非同期メソッドの対応状況をまとめると、以下のような感じになります。

ASP.NET WebForms OK
ASP.NET MVC OK
ASP.NET Web API OK
WebSocket OK
WCF OK
WCF Data Services N/A

 

Comments (1)

  1. Matteo says:

    初めまして

    現在、下記サイトを作っているのですが、

    http://www.wiredblogs.com/landing.aspx

    人が足らないため、協力者を求めています。

    このサイトに関しては、google mapのAPIの知識、デザイン作成が必要となっております。

    もし、可能であれば、ご協力をお願いしたいと思って連絡を致しました。

    可能であれば、ご連絡をお願いします。

    マッテオ・マルティーニ

    matteomartini72(at)yahoo(dot)com

Skip to main content