苏飞论坛

标题: 网站[高并发]下使用[静态方法]会造成页面无法访问的解决方案 [打印本页]

作者: 站长苏飞    时间: 2013-7-17 18:25
标题: 网站[高并发]下使用[静态方法]会造成页面无法访问的解决方案
先来说明一下问题,2楼开始说解决方案
委托delegate与Dictionary实现action选择器大家先看看我上一次的文章
大致如下
[C#] 纯文本查看 复制代码
//定义一个委托
   private delegate void actionByMain(HttpContext context);
   //定义一Key,Value的键值对,大家注意这里把委托放进去了
   private static Dictionary<string, actionByMain> mainList = new Dictionary<string, actionByMain>();
   public void ProcessRequest(HttpContext context)
   {
       string action = string.Empty;
       if (string.IsNullOrEmpty(context.Request["action"]))
       {
           return;
       }
       action = context.Request["action"];
       //先检查一下有没有这个action
       if (mainList.ContainsKey(action))
       {
           mainList[action](context); return;
       }
   }
   //添加action对应方法的列表
   static void addMainList()
   {
       mainList.Add("post1", delegate(HttpContext context)
      {
          context.Response.Write("成功");
      });
       mainList.Add("post2", delegate(HttpContext context)
     {
         context.Response.Write("成功");
     });
       mainList.Add("post3", delegate(HttpContext context)
     {
         return;
     });
   }

就是这样的写法,容易出现问题
在高并发的时候经常出现页面无法访问的问题
其实我也不敢确定是不是这里的问题但是有以下几点可以确定
可能会有人说,如果不使用这种而是直接调用静态方法呢,是一样的效果。这点我测试过。
第一点换成普通类型


换成如下写法就不会出现这样的问题
[C#] 纯文本查看 复制代码
//取出action的值
string action = context.Request["action"];
if (action == "post1")
{
   //调用第一个Post方法
}
else if (action == "post2")
{
   //调用第一个Post方法
}
else if (action == "post3")
{
   //调用第一个Post方法
}
else
{
   //调用在没有找到action对应方法时执行
}[/code]
方法如下
[mw_shl_code=csharp,true] private void post1()
{

}
private void post2()
{

}
private void post3()
{

}

第二点:访问量比较小时,本页面日访问量低于2万Pv的就不要测试了,应该不会有效果


   这个我经过测试确定是这样的
第三点:IIS设置应用程序池不回收时,也不会出现这样的问题,但是只要设置回收就会出现

这一点虽然我不知道是什么情况但是实际效果就是这样
总结:
这个问题一直得不到一个完整合理的解释,希望博客园的高手们进来看看,帮助解释一下是什么问题造成的。
我的理解:
     我认为会不会是因为正好在高并发的时候IIS开始回收,在回收时或者是在页面重建时,就在这一刻是无法访问的,而且正好让用户访问了,而这个状态正好被浏览器记录了下来,然后就不能访问了,
第四点:因为经过我们的测试,只要清理一下浏览器的缓存或者是重启电脑路由之后就基本上又可以正常访问了
这种现像很难解释啊,
所以没有办法后来我又修改成了。普通的写法。
大家讨论一下看看具体是那里的问题
我确实是解释不了这种现像了。

作者: 站长苏飞    时间: 2013-7-17 18:25
[code=csharp]addMainList()是在什么地方调用的?构造函数?mainList是static的非线程安全的dictionary,增删改mainList的时候要加锁! 你这个例子,最简单的是把addMainList()放到静态构造函数里面调用 static ClassName
(){addMainList();}
或者增删改mainList的时候加锁
static object LockObj=new object();
static void addMainList()
{
lock(LockObj)
{
mainList.Add("post1", delegate(HttpContext context)
{
context.Response.Write("成功");
});
mainList.Add("post2", delegate(HttpContext context)
{
context.Response.Write("成功");
});
mainList.Add("post3", delegate(HttpContext context)
{
return;
});
}
}[/code]
作者: 站长苏飞    时间: 2013-7-17 18:25
高并发的时候才出问题,那是因为高并发的时候会出现多个线程同时操作mainList, 由于它不是线程安全的,所以就报错了! 加锁就是为了限制在一个线程操作它的时候,其他线程等待它操作完了再对它操作!
你这个mainList其实是很简单的字典而已,其实不要把它声明为static就不会有线程的安全问题!很多方法可以处理的,看你需要怎么用而已
作者: 站长苏飞    时间: 2013-7-17 18:26
基本上可以判断是多线程冲突导致,你列举的几点都很符合多线程冲突场景。
第四点的话,可能是浏览器接收到IIS返回的错误信息,导致浏览器缓存了一些错误信息。具体问题,还需要你具体说明一下浏览器无法访问的错误信息。

static void addMainList() 这个方法是什么时候调用的?至少看起来不是静态构造函数,所以没有加锁的话,应该是会导致错误的。

if (mainList.ContainsKey(action))
       {
           mainList[action](context); return;
       }
mainList 应该不会因为回收导致为null,只要这个类还在运行,是不会回收的。但Dictionary本身不是线程安全的,所以这个用法有错。Dictionary的读写都不是线程安全的,包括ContainsKey方法。
作者: 站长苏飞    时间: 2013-7-17 18:32
我们说一个数据结构是线程安全指的是同一时间只有一个线程可以改写它。这样即使多个线程来访问它,它也不会产生对线程来说很意外的数据。
C#中的Dictionary不是线程安全的,我在下面这个例子中,把一个Dictionary对象作为了全局的static变量。会有多个线程来访问它。所以我需要包装一下.net自带的Dictionrary.
发生冲突的部分无非是写的地方,所以在离写Dictionary最近的地方加一个锁。其他的外层代码可以自带的Dictionary相同了。
我们看Dictionary的实现接口,

(, 下载次数: 133)