个人喜欢精简代码,不能接受冗长且繁复的代码,很喜欢使用static变量,直接引用,不需再次申明,在桌面开发中没有问题,但是B/S架构中稍不注意static变量将成为一颗定时炸弹。在FWDCMS内容管理系统中问题出现的特别严重,首先是在缓存类中,其次是在模版引擎里,单用户访问时不会有任何问题,并发访问时BUG频频出现。
下面以公共缓存类为示例,分析问题并解决掉它。
代码重现
截取缓存公共类代码如下:
- using System;
- using System.Collections;
- using System.Web.Caching;
- namespace FWD_CMS.Common
- {
- public class FWD_Cache
- {
- private static string LocalCacheName;
- private static string CacheName = System.Configuration.ConfigurationSettings.AppSettings["CacheName"] + "FORWARD";
- private static object Cache_Data = null;
- private static double Reloadtime = 7 * 24 * 60;
- //缓存名称
- public static string Name
- {
- set { LocalCacheName = value.ToUpper(); }
- }
- public static object Value
- {
- //根据NAME取缓存
- get
- {
- if (LocalCacheName != null || LocalCacheName.Length > 0)
- {
- if (System.Web.HttpRuntime.Cache[String.Format("{0}_{1}", CacheName, LocalCacheName)] != null)
- {
- Cache_Data = (object)System.Web.HttpRuntime.Cache[String.Format("{0}_{1}", CacheName, LocalCacheName)];
- }
- }
- return Cache_Data;
- }
- //根据NAME定义缓存
- set
- {
- if (value == null) return;
- if (LocalCacheName != null || LocalCacheName.Length > 0)
- {
- System.Web.HttpRuntime.Cache.Insert(String.Format("{0}_{1}", CacheName, LocalCacheName), value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(Reloadtime));
- }
- }
- }
- }
- }
使用方法:FWD_Cache.Name="FWD_Class";FWD_Cache.Value=值;值=FWD_Cache.Value;先申明缓存的名称,然后进行取值或赋值操作。
上面的代码简单,调用起来也非常方便,看似都是没有问题的,但是它就是一颗定时炸弹!{FWD_PAGER}
为什么是定时炸弹
这还要从Asp.net的运行机制谈起。在CS模式软件开发过程中,我们通常不会关心应用程序是在哪里运行的,变量存放在哪里,客户端程序就运行在客户端,服务器端程序就运行在服务器端,一般情况下,二者除了数据库中的数据外基本没有其他共享的东西。所以这时客户端的用户大可放心的使用static变量,因为它们就存放在客户端程序中。
于是我们就习惯的在做BS模式的页面时也用static变量,殊不知Asp.net中的static已不同于CS中的static。原因很简单,就是因为在Asp.net中所有的用户将使用同一个static变量。这就意味着每一个使用该页面的用户对该变量的操作将会影响到其他用户。
在上面的代码中如果两个用户同时运行,A用户将FWD_Cache.Name设为A然后取缓存值,B用户将FWD_Cache.Name设为B然后取缓存值,就在A将Name刚刚设置成A的时候,B用户刚好又把Name设为了B,A用户将取到是缓存名为B的缓存值,接下来A用户的数据就完全与它预想的不同了,报错是必然的,如果取到的值是需要参与下一步运算……
两种解决办法
第一种:Name与其中的方法都去掉static,实例化类,然后再调用,操作代码必须变成FWD_Cache _cache=new FWD_Cache();_cache.name="xxx";_cache.Value=xxx就不会有问题了。
第二种:将Name这个属性彻底不要,缓存的赋值与取值都写成方法,将缓存名做为参数传给方法也是可行的。例如赋值代码可能会变成FWD_Cache.SetValue(String CacheName){...},取值代码变成FWD_Cache.GetValue(String CacheName){...},如何在FWD_Cache类中去实现SetValue与GetValue两个方法不在赘述。
最后提示:在开发中请慎用static变量。