优化你的DiscuzNT3.0,让它跑起来(6)在线人数和Regex.IsMatch()引发的hang

注:本文仅针对 DiscuzNT3.0, sqlserver 2000版本,其他版本请勿对号入座。

你没看错标题,的确是 在线人数和Regex.IsMatch()引发的hang。事情是这样的,就在今天我们的论坛出现的挂起问题,当时刚好赶上了抓dump文件。于是就有了今天这篇文章。 

我们先用windbg看看论坛当时在干什么吧。

1. 打开文件,运行 .load sos, 因为是hang,所以当然是要运行 !syncblk , 下面是运行结果:

0:000> .load sos

0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
  202 000fe87c            1         1 1c3d2c98   d28  48   02413704System.Collections.Generic.LinkedList`1[[System.Text.RegularExpressions.CachedCodeEntry, System]]
-----------------------------
Total           419
CCW             2
RCW             0
ComClassFactory 0
Free            354

 

从上面看到,持有锁的线程是48 ,持有的对象是System.Collections.Generic.LinkedList 

 

2. 们看看线程48在干什么, ~48s 切换到线程,!clrstack 看看执行的代码。

0:000> ~48s
eax=000006d7 ebx=1c3d2c98 ecx=000fe87c edx=044ec5c3 esi=00000354 edi=00000000
eip=7c9585ec esp=1d8eeee4 ebp=1d8eef54 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
7c9585ec c3              ret
0:048> !clrstack
OS Thread Id: 0xd28 (48)
ESP       EIP     
1d8ef060 7c9585ec [GCFrame: 1d8ef060] 
1d8ef0fc 7c9585ec [HelperMethodFrame: 1d8ef0fc] System.Threading.Monitor.Enter(System.Object)
1d8ef150 7a4aed85 System.Text.RegularExpressions.Regex.LookupCachedAndUpdate(System.String)
1d8ef180 7a4aec91 System.Text.RegularExpressions.Regex..ctor(System.String, System.Text.RegularExpressions.RegexOptions, Boolean)
1d8ef1b4 7a4bf480 System.Text.RegularExpressions.Regex.IsMatch(System.String, System.String)
1d8ef1c4 1ba6c3cc Discuz.Common.TypeConverter.StrToInt(System.String, Int32)
1d8ef1e0 1ba6f6c2 Discuz.Common.TypeConverter.ObjectToInt(System.Object)
1d8ef1e4 1d50a215 Discuz.Data.OnlineUsers.LoadSingleOnlineUser(System.Data.IDataReader)
1d8ef1f4 1d50a111 Discuz.Data.OnlineUsers.GetOnlineUserCollection()
1d8ef204 1d509f73 Discuz.Forum.OnlineUsers.GetOnlineUserCollection(Int32 ByRef, Int32 ByRef, Int32 ByRef, Int32 ByRef)
1d8ef25c 1d30b291 Discuz.Web.website.ShowPage()
1d8ef274 1ba6d837 Discuz.Forum.PageBase..ctor()
1d8ef318 1d37e2d1 Discuz.Web.website..ctor()
1d8ef324 1d37e0f0 ASP.aspx_2_website_aspx..ctor()
。。。。。。这里省略若干字

-------------------------- 

我们从上面看到程序调用了 

Discuz.Common.TypeConverter.StrToInt() 这个方法,然后进入 
System.Text.RegularExpressions.dll,最后停留在
System.Text.RegularExpressions.Regex.LookupCachedAndUpdate() 方法, 我们从dnt3.0的程序一步步来看看。

 

 1         /// <summary>
 2         /// 将对象转换为Int32类型
 3         /// </summary>
 4         /// <param name="str">要转换的字符串</param>
 5         /// <param name="defValue">缺省值</param>
 6         /// <returns>转换后的int类型结果</returns>
 7         public static int StrToInt(string str, int defValue)
 8         {
 9             if (string.IsNullOrEmpty(str) || str.Trim().Length >= 11 || !Regex.IsMatch(str.Trim(), @"^([-]|[0-9])[0-9]*(\.\w*)?$"))
10                 return defValue;
11 
12             int rv;
13             if (Int32.TryParse(str, out rv))
14                 return rv;
15 
16             return Convert.ToInt32(StrToFloat(str, defValue));

17         } 


请出reflector 

1 public static bool IsMatch(string input, string pattern)
2 {
3     return new Regex(pattern, RegexOptions.None, true).IsMatch(input);

4 } 

 

继续reflector

 1 private Regex(string pattern, RegexOptions options, bool useCache)
 2 {
 3     CachedCodeEntry cachedAndUpdate = null;
 4     string threeLetterWindowsLanguageName = null;
 5     if (pattern == null)
 6     {
 7         throw new ArgumentNullException("pattern");
 8     }
 9     if ((options < RegexOptions.None) || ((((int) options) >> 10!= 0))
10     {
11         throw new ArgumentOutOfRangeException("options");
12     }
13     if (((options & RegexOptions.ECMAScript) != RegexOptions.None) && ((options & ~(RegexOptions.CultureInvariant | RegexOptions.ECMAScript | RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase)) != RegexOptions.None))
14     {
15         throw new ArgumentOutOfRangeException("options");
16     }
17     if ((options & RegexOptions.CultureInvariant) != RegexOptions.None)
18     {
19         threeLetterWindowsLanguageName = CultureInfo.InvariantCulture.ThreeLetterWindowsLanguageName;
20     }
21     else
22     {
23         threeLetterWindowsLanguageName = CultureInfo.CurrentCulture.ThreeLetterWindowsLanguageName;
24     }
25     string[] strArray = new string[] { ((int) options).ToString(NumberFormatInfo.InvariantInfo), ":", threeLetterWindowsLanguageName, ":", pattern };
26     string key = string.Concat(strArray);
27     cachedAndUpdate = LookupCachedAndUpdate(key);
28     this.pattern = pattern;
29     this.roptions = options;
30     if (cachedAndUpdate == null)
31     {
32         RegexTree t = RegexParser.Parse(pattern, this.roptions);
33         this.capnames = t._capnames;
34         this.capslist = t._capslist;
35         this.code = RegexWriter.Write(t);
36         this.caps = this.code._caps;
37         this.capsize = this.code._capsize;
38         this.InitializeReferences();
39         t = null;
40         if (useCache)
41         {
42             cachedAndUpdate = this.CacheCode(key);
43         }
44     }
45     else
46     {
47         this.caps = cachedAndUpdate._caps;
48         this.capnames = cachedAndUpdate._capnames;
49         this.capslist = cachedAndUpdate._capslist;
50         this.capsize = cachedAndUpdate._capsize;
51         this.code = cachedAndUpdate._code;
52         this.factory = cachedAndUpdate._factory;
53         this.runnerref = cachedAndUpdate._runnerref;
54         this.replref = cachedAndUpdate._replref;
55         this.refsInitialized = true;
56     }
57     if (this.UseOptionC() && (this.factory == null))
58     {
59         this.factory = this.Compile(this.code, this.roptions);
60         if (useCache && (cachedAndUpdate != null))
61         {
62             cachedAndUpdate.AddCompiled(this.factory);
63         }
64         this.code = null;
65     }

66 } 

 

这个代码量较大,找到关键点 LookupCachedAndUpdate 

private static CachedCodeEntry LookupCachedAndUpdate(string key)
{
    
lock (livecode)        
    {
for (LinkedListNode<CachedCodeEntry> node = livecode.First; node != null; node = node.Next)
        {
            
if (node.Value._key == key)
            {
                livecode.Remove(node);
                livecode.AddFirst(node);
                
return node.Value;
            }
        }
    }
    
return null;

} 

 

终于找到这个lock的对象了,看看他是什么类型的,和我们通过windbg看到的一样吗

internal static LinkedList<CachedCodeEntry> livecode;  

果然一样, 这下应该放心了,就是这里的lock引起了hang,但是我们应该经常用Regex.IsMatch()的,也没见引起这个问题啊,为什么这里???

我们看看在线人数有多少,如果在线人数比较多,访问的人数也多,那可能性就很大了,我们来看看到底有多少人在线。

 

运行 !dso,看看本线程对象。

0:048> !dso
OS Thread Id: 0xd28 (48)
ESP/REG  Object   Name
1d8ef094 02413704 System.Collections.Generic.LinkedList`1[[System.Text.RegularExpressions.CachedCodeEntry, System]]
1d8ef0b8 074b8c54 System.String    0:CHS:^([-]|[0-9])[0-9]*(\.\w*)?$
1d8ef0bc 074b94ac System.String    114.247.10.127
1d8ef0d0 074b94ac System.String    114.247.10.127
1d8ef150 02413704 System.Collections.Generic.LinkedList`1[[System.Text.RegularExpressions.CachedCodeEntry, System]]
1d8ef170 074b8c20 System.Text.RegularExpressions.Regex
1d8ef184 0a3fcd1c System.String    ^([-]|[0-9])[0-9]*(\.\w*)?$
1d8ef190 074b8c54 System.String    0:CHS:^([-]|[0-9])[0-9]*(\.\w*)?$
1d8ef19c 074b8c08 System.String    -1
1d8ef1a0 074b8c20 System.Text.RegularExpressions.Regex
1d8ef1a4 0a3fcd1c System.String    ^([-]|[0-9])[0-9]*(\.\w*)?$
1d8ef1b4 068e2dc4 Discuz.Common.Generic.List`1[[Discuz.Entity.OnlineUserInfo, Discuz.Entity]]
1d8ef1b8 074b8c08 System.String    -1
1d8ef1d4 074b8bac Discuz.Entity.OnlineUserInfo
1d8ef1d8 068e2f64 System.Data.SqlClient.SqlDataReader
。。。。。。省略若干字

----------------------

 

找到上面的  

Discuz.Common.Generic.List 的地址 
068e2dc4 , 运行 !do 
068e2dc4 

 

0:048> !do 068e2dc4 
Name: Discuz.Common.Generic.List`1[[Discuz.Entity.OnlineUserInfo, Discuz.Entity]]
MethodTable: 1ceb7084
EEClass: 1bb39dd0
Size: 28(0x1c) bytes
 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\9932c999\b8dfff59\assembly\dl3\21614a1e\486ed665_6d88ca01\Discuz.Common.DLL)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79124228  400099d        4      System.Object[]  0 instance 0f1b254c _items
790fed1c  400099e        c         System.Int32  0 instance      472 _size
790fed1c  400099f       10         System.Int32  0 instance      472 _version
790f9c18  40009a0        8        System.Object  0 instance 00000000 _syncRoot
79124228  40009a1        0      System.Object[]  0   shared   static _emptyArray
    >> Domain:Value dynamic statics NYI
 000e1008:NotInit dynamic statics NYI
 00107038:NotInit  <<
790fed1c  400000c       14         System.Int32  0 instance        0 _fixedsize

 

从上面的size可以看到在线人数是472人,就是说这个48这个线程要lock 472 次,如果有n个人访问那后面的人真的要等不少时候了。

话说StrToInt()这个方法为什么要用Regex.IsMatch()呢,string 转换成 int 一般 int.TryParse()也足够了。不过从这里我才发现Regex.IsMatch() 里面原来还有个lock,不然还真不知道,也算是收获不小啊。

 

 

 

posted @ 2011-06-26 21:11  鸽子飞扬  阅读(1932)  评论(9编辑  收藏  举报