(如果只是想使用应用程序状态的值,问题是很简单的.我个人坚持对待一个问题不能只在表面上打转,要深入进去把问题搞明白。所以就记录这点东西)
先介绍两个概念:
网络场:所谓的网络场是指应用程序由多台服务器承载的情况。
网络园:是指应用程序被同一台服务器上的多个进程承载的情况。
应用程序状态概述
ASP.NET 应用程序是具有以下特征的所有文件、页、处理程序、模块和代码的总和:它们都驻留在给定虚拟目录及其子目录中;它们都可以由用户通过虚拟目录层次结构来请求。
任何客户端首次在特定 ASP.NET 应用程序的虚拟目录命名空间中请求 URL 资源时,都将创建 HttpApplicationState 类的实例。存储在计算机上的每个 Web 应用程序都是这种情况。对这种按应用程序的实例的访问权限是通过名为 Application的 HttpContext 属性提供的。所有 HttpModule 和 HttpHandler(例如 ASP.NET 页)都具有对上下文的实例的访问权限,因此具有在给定 Web 请求过程中对 Application 属性的访问权限。
使用应用程序状态
实际上,应用程序状态变量是给定 ASP.NET 应用程序的全局变量。像客户端应用程序开发人员一样,ASP.NET 程序员应该始终考虑将任何对象存储为全局变量所具有的影响。
关于这一点,下面的几个问题非常重要:
· 在应用程序状态中进行存储的内存影响。存储在应用程序状态中的变量占用的内存在移除或替换该值之前不被释放,这与单独的 Web 页不同,对于 Web 页,所有资源在 Web 请求结束时都将强行释放。例如,若在应用程序状态中永久保留很少使用的 10 MB 记录集,就算不上不是对系统资源的高效使用。
· 在多线程服务器环境中存储和访问全局变量所涉及的并发和同步问题。应用程序中的多个线程可以同时访问存储在应用程序状态中的值。您应该始终注意确保如果应用程序范围的对象是自由线程的,则它包含内置的同步支持。所有面向公共语言运行库的自定义对象都是自由线程的。如果应用程序范围的对象不是自由线程的,则您必须确保在其周围编写显式同步方法代码以避免死锁、争用状态和访问冲突。
· 在多线程服务器环境中存储和访问全局变量所涉及的可缩放性问题。只要试图写入或更新文件,就应该使用锁。保护全局资源的锁本身就是全局的,运行在访问全局资源的多个线程上的代码最终对这些锁产生竞争。这会导致操作系统在锁可用之前阻塞辅助线程。在高负载的服务器环境中,这种阻塞可能导致系统上严重的线程颠簸。在多处理器系统上,它可能导致处理器不能充分利用(因为在理论上,在等待共享锁时,可以停止某个处理器的所有线程),从而导致整体可缩放性的严重下降。
· 存储在应用程序状态中的信息的生命周期问题。在应用程序执行过程中,承载基于 .NET 的应用程序的 .NET Framework 应用程序域或进程可能会随时被强行停止并销毁,原因可能是出现了故障、进行了代码更新、重新启动了预定的进程等。因为存储在应用程序状态中的数据不是持久的,所以如果包含它的宿主被损坏,数据也将丢失。如果希望状态在这些类型的故障中保存下来,您应该将其存储在数据库或其他可持久的存储区中。
· 应用程序状态不能跨网络场或网络园共享,所谓的网络场是指应用程序由多台服务器承载的情况;网络园是指应用程序被同一台服务器上的多个进程承载的情况。这两种情况任何一个中的应用程序状态中存储的变量只对于应用程序在其中运行的特定进程是全局的。每个应用程序进程都可以具有不同的值。因此,您不能依靠应用程序状态来存储唯一值或更新全局计数器,例如在网络场和网络园情况下。
虽然存在这些问题,但是设计良好的应用程序级别的变量在 Web 应用程序中可以发挥强大的功能。您可以进行信息的一次性(即不经常的)加载和计算,然后使用应用程序状态对其进行缓存,从而在稍后的 Web 请求中可以从内存中快速对其进行访问。
应用程序状态集合
HttpApplicationState类公开两个状态集合: Contents 和 SaticObjects
Contents集合公开已直接通过代码添加到应用程序状态集合的所有变量项。例如:
// C# code from within a page, a handler, or Global.asax.
Application["Message"] = " MyMsg";
Application["AppStartTime"] = DateTime.Now;
还可以使用应用程序对象的 Contents属性访问这些变量,如:
Application.Contents["Message"] = " MyMsg";
Application.Contents["AppStartTime"] = DateTime.Now;
SaticObjects集合公开所有已经通过 Global.asax 文件中带有应用程序范围的 <
object runat="server">
标记添加到应用程序状态集合的变量项。例如:
' Global.asax definition.
<object runat="server" scope="application" ID="MyInfo" PROGID="MSWC.MYINFO">
</OBJECT>
对象不能从 ASP.NET 应用程序内的其他任何地方添加到 StaticObjects 集合中。如果您试图直接通过代码添加对象,集合将引发 NotSupportedException异常。
请注意,.NET 页编译器在页编译时自动将成员引用插入存储在 StaticObjects 集合中的所有对象,以便开发人员可以在页请求时访问这些应用程序对象,而无需引用Application 集合。例如:
<html>
</body>
Application Level Title: <%= MyInfo.Title %>
<body>
</html>
应用程序状态同步
应用程序中的多个线程可以同时访问存储在应用程序状态中的值。因此,当您创建需要访问应用程序状态值的对象时,必须始终确保该应用程序状态对象是自由线程的并执行它自己的内部同步,要不就执行手动同步步骤以防止出现争用状态、死锁或访问冲突。
HttpApplicationState 类提供两种方法 Lock 和 Unlock,一次只允许一个线程访问应用程序状态变量。
对 Application 对象调用 Lock 会导致 ASP.NET 阻止运行在其他辅助线程上的代码试图访问应用程序状态中的任何对象。只有当调用 Lock 的线程对 Application 对象调用相应的 Unlock 方法时才解除对这些线程的阻塞。
下面的代码示例演示使用锁定以防止出现争用状态。
// C# code from within a page, a handler, or Global.asax.
Application.Lock();
Application["SomeGlobalCounter"] = (int)Application["SomeGlobalCounter"] + 1;
Application.UnLock();
如果没有显式调用 Unlock,当请求完成、请求超时或请求执行过程中出现未处理的错误并导致请求失败时,.NET Framework 将自动移除锁。这种自动取消锁定会防止应用程序出现死锁。