次の方法で共有


アプリケーション状態

HttpContext オブジェクトの Application プロパティを通じて最も一般的にアクセスされる HttpApplicationState クラスを使用すると、アプリケーション全体で情報を共有できます。このクラスは、.NET Framework オブジェクトと、複数のクライアントからの複数の Web 要求に関するスカラ値の両方を格納するために使用できるオブジェクトのキー値のディクショナリを公開します。

ここでは、アプリケーション状態の概要、アプリケーション状態の使用方法、アプリケーション状態コレクションの概要、およびアプリケーション状態の同期について説明します。

アプリケーション状態の概要

ASP.NET アプリケーションは、特定の仮想ディレクトリやそのサブディレクトリ内に存在し、ユーザーがその仮想ディレクトリ階層構造を通じて要求できる、すべてのファイル、ページ、ハンドラ、モジュール、およびコードの集合です。

たとえば、会社のイントラネット用に投資利益を計算するアプリケーションを開発する場合は、Web サーバー上の \Invest という名前の仮想ディレクトリにそのアプリケーションを公開できます。このようなアプリケーション用のディレクトリ構造は、次のようになります。

\Invest

   \bin

   \image

   \xml

特定の ASP.NET アプリケーションの仮想ディレクトリ名前空間内の URL リソースを任意のクライアントがそれぞれ最初に要求したときに、HttpApplicationState クラスのインスタンスが作成されます。このインスタンスは、コンピュータに格納されている Web アプリケーションごとに作成されます。このアプリケーション別のインスタンスには、HttpContext オブジェクトの Application という名前のプロパティを通じてアクセスできます。ASP.NET ページなどのすべての HTTPModule および HTTPHandler は、コンテキストのインスタンスにアクセスできるため、特定の Web 要求中に Application プロパティにアクセスできます。

ASP.NET には、次のアプリケーション状態サポートが用意されています。

  • 使いやすい状態機能。ASP の旧バージョンと互換性があり、.NET でサポートされるすべての言語に対応しています。また他の .NET Framework API との一貫性もあります。
  • アプリケーション状態のディクショナリ。アプリケーション内で呼び出されたすべての要求ハンドラで利用できます。ページだけがアプリケーション状態にアクセスできるインターネット インフォメーション サービス (IIS: Internet Information Services) や ASP の旧バージョンとは異なり、すべての IHttpHandler インスタンスや IHttpModule インスタンスは、ディクショナリ内のグローバル変数を格納および取得できます。
  • 単純でわかりやすい同期機構。開発者はアプリケーション状態に格納された変数への同時アクセスを簡単に調整できるようになります。
  • アプリケーション状態値。状態を格納したアプリケーションのコンテキスト内で実行中のコードからだけアクセスできます。システムで実行中の他のアプリケーションは、アプリケーション状態値にアクセスしたり、アプリケーション状態値を変更したりできません。

アプリケーション状態にアクセスする最も一般的な方法は、Page オブジェクトの Application プロパティを使用することです。

アプリケーション状態の使用

アプリケーション状態変数は、実際は、特定の ASP.NET アプリケーションのグローバル変数です。クライアント側アプリケーション開発者と同様、ASP.NET プログラマは、任意のデータをグローバル変数として格納する場合の影響について、常に考慮する必要があります。

グローバル変数として格納する場合の、特に重要な問題点を次に示します。

  • 任意のデータをアプリケーション状態に格納した場合のメモリへの影響。Web 要求の終了時にすべてのリソースが破棄される個別の Web ページの場合とは異なり、アプリケーション状態に格納された変数によって占有されたメモリは、値が削除または置換されるまで解放されません。たとえば、使用頻度の低い 10 MB のレコードセットを永久にアプリケーション状態に保持することは、システム リソース利用において最善の方法ではありません。このような極端な例では、ASP.NET キャッシュを使用して、より優れた解決方法を見つけることができます。
  • マルチスレッド サーバー環境でグローバル変数の格納およびグローバル変数へのアクセスを行った場合の同時実行および同期への影響。アプリケーション内の複数のスレッドが、アプリケーション状態に格納された値に同時にアクセスできます。アプリケーション スコープのオブジェクトがフリー スレッドの場合は、そのオブジェクトが組み込み同期サポートを含んでいることを確認するように常に注意する必要があります。共通言語ランタイムを対象とするすべてのカスタム オブジェクトは、フリー スレッドです。アプリケーション スコープのオブジェクトがフリー スレッドでない場合は、デッドロック状態、競合状態、およびアクセス違反を回避するために、そのオブジェクトに関連する明示的な同期メソッドがコーディングされていることを確認する必要があります。
  • マルチスレッド サーバー環境でグローバル変数の格納およびグローバル変数へのアクセスを行った場合のスケーラビリティへの影響。ファイルの書き込みまたは更新を行うときは、必ずロックを使用する必要があります。グローバル リソースを保護するロックはそれ自体がグローバルであるため、グローバル リソースにアクセスする複数のスレッドで実行されているコードは、最終的にはそれらのロックで競合することになります。この競合によって、オペレーティング システムは、ロックが利用できるようになるまでワーカー スレッドをブロックすることになります。高負荷サーバー環境では、このブロッキングによって、システムで重大なスレッド スラッシングが発生する可能性があります。マルチプロセッサ システムでは、プロセッサが十分に利用されず (理論上、共有ロックを待機する間にプロセッサ用のすべてのスレッドが一時停止する可能性があるため)、全体のスケーラビリティが大きく低下する可能性があります。
  • アプリケーション状態に格納された情報による有効期間への影響。.NET Framework アプリケーション ドメインまたは .NET ベースのアプリケーションを管理するプロセスは、アプリケーションの実行中に破棄される可能性があります (クラッシュ、コードの更新、スケジュールされたプロセスの再起動などの結果)。アプリケーション状態に格納されたデータは永続的ではないため、アプリケーション状態を含むホストが破棄されると、そのアプリケーション状態も失われます。この種のエラーが発生しても状態が存続できるようにするには、状態をデータベースやその他の永続的なストアに格納する必要があります。
  • アプリケーションの状態は、Web ファーム (1 つのアプリケーションが複数のサーバーでホストされる) または Web ガーデン (1 つのアプリケーションが同じサーバー上の複数のプロセスでホストされる) 全体にわたって共有されるわけではありません。アプリケーション状態に格納された変数は、Web ファームまたは Web ガーデンのどちらの場合でも、アプリケーションが実行されている特定のプロセスに対してだけグローバルです。各アプリケーション プロセスは、さまざまな値を持つことができます。そのため、たとえば、Web ファームと Web ガーデンの場合に、アプリケーション状態に依存して、一意の値を格納したり、グローバル カウンタを更新したりできません。

このような問題があっても、適切にデザインされたアプリケーション レベルの変数は、Web アプリケーションで力を発揮します。1 回だけ (または少ない頻度で) 情報の読み込みと計算を行い、次に、高速化のためにアプリケーション状態を使用してその情報をキャッシュし、その後の Web 要求中にインメモリ アクセスを行うことができます。

たとえば、株式市場の Web サイトは、日中、データベースから 5 分ごとに大量の金融株式情報 (40 MB のデータなど) をフェッチし、次にその情報をアプリケーション状態にキャッシュします。以後、このアプリケーション状態では、どの検索要求でもその情報にアクセスできます。その結果、着信要求にプロセス間ラウンドトリップ、コンピュータ間ラウンドトリップ、またはデータベースでのラウンドトリップが必要ないため、要求ごとのパフォーマンスが大幅に向上します。一方、サイズの大きい、一時的なデータ ブロック用のリソースの最善の利用方法は、キャッシュを使用することです。

アプリケーション状態コレクション

HttpApplicationState クラスは、Contents および StaticObjects という 2 つの状態コレクションを公開します。

Contents コレクションは、コードを通じて直接アプリケーション状態コレクションに追加されたすべての変数項目を公開します。次に例を示します。

'  Visual Basic code from within a page, a handler, or Global.asax.
Application("Message") = "MyMsg"
Application("AppStartTime") = Now
[C#]
// C# code from within a page, a handler, or Global.asax.
Application["Message"] = " MyMsg";
Application["AppStartTime"] = DateTime.Now;

ASP の旧バージョンとの互換性を保つため、次の例で示すとおり、アプリケーション オブジェクトの Contents プロパティを通じても、これらの値にアクセスできます。

' Visual Basic code from within a page, a handler, or Global.asax.
Application.Contents("Message") = " MyMsg"
Application.Contents("AppStartTime") = Now
[C#]
// Visual Basic code from within a page, a handler, or Global.asax.
Application.Contents["Message"] = " MyMsg";
Application.Contents["AppStartTime"] = DateTime.Now;

StaticObjects コレクションは、Global.asax ファイル内のアプリケーション スコープを含む <object runat="server"> タグを通じてアプリケーション状態コレクションに追加されたすべての変数項目を公開します。次に例を示します。

' Global.asax definition.
<object runat="server" scope="application" ID="MyInfo" PROGID="MSWC.MYINFO">
</OBJECT>

オブジェクトは、ASP.NET アプリケーション内でだけ StaticObjects コレクションに追加できます。StaticObjects コレクションは、コードを通じて直接オブジェクトを追加しようとすると、NotSupportedException をスローします。

.NET ページ コンパイラは、ページ コンパイル時に、メンバ参照を StaticObjects コレクション内に格納されたすべてのオブジェクトに自動的に挿入します。このため、開発者は、ページ要求時に Application コレクションを参照せずにこれらのアプリケーション オブジェクトにアクセスできます。次に例を示します。

<html>
   </body>
      Application Level Title: <%= MyInfo.Title %>
   <body>
</html>

アプリケーション状態の同期

アプリケーション内の複数のスレッドが、アプリケーション状態に格納された値に同時にアクセスできます。したがって、アプリケーション状態の値にアクセスする必要がある任意のスレッドを作成する場合は、アプリケーション状態オブジェクトがフリー スレッドであり、競合状態、デッドロック状態、およびアクセス違反を防ぐために独自の内部同期手順を実行するか、または手動同期手順を実行することを常に確認する必要があります。

HttpApplicationState クラスには、一度に 1 つのスレッドだけがアプリケーション状態変数にアクセスできるようにする LockUnlock という 2 つのメソッドが用意されています。

Application オブジェクトに対する Lock を呼び出すと、ASP.NET は、他のワーカー スレッドで実行中のコードによる、アプリケーション状態内の任意の値にアクセスしようとする試みをブロックします。これらのスレッドは、Lock を呼び出したスレッドが、それに対応する Unlock メソッドを Application オブジェクトに対して呼び出したときにだけ、ロック解除されます。

ロック機能を使用して競合状態を防ぐ方法を次のコード例で示します。

' Visual Basic code from within a page, a handler, or Global.asax.
Application.Lock()
Application("SomeGlobalCounter") = _
   CType(Application("SomeGlobalCounter"), Integer) + 1
Application.UnLock()
[C#]
// C# code from within a page, a handler, or Global.asax.
Application.Lock();
Application["SomeGlobalCounter"] =
   (int)Application["SomeGlobalCounter"] + 1;
Application.UnLock();

Unlock を明示的に呼び出さなかった場合、.NET Framework は、要求が完了またはタイムアウトしたとき、あるいは要求の実行中に処理されないエラーが発生し、要求が失敗したときに、ロックを自動的に解除します。この自動ロック解除機能によって、アプリケーションがデッドロック状態になることを防止できます。

参照

ASP.NET の状態管理 | ASP.NET アプリケーション