新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     >>计算机科学论坛<<     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论.NET,C#,ASP,VB技术
    [返回] 计算机科学论坛计算机技术与应用『 Dot NET,C#,ASP,VB 』 → 多线程:从对象池(object pool)谈同步(syncronization) ——一个调试的问题 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 7163 个阅读者浏览上一篇主题  刷新本主题   平板显示贴子 浏览下一篇主题
     * 贴子主题: 多线程:从对象池(object pool)谈同步(syncronization) ——一个调试的问题 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 Dot NET,C#,ASP,VB 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 多线程:从对象池(object pool)谈同步(syncronization) ——一个调试的问题

    最近,刚看过Jeffry Richter的《Programming Application for Microsoft Windows 4th Edition》。眼下正看《C# Threading HandBook》。看了前三章,觉得很不错。觉得这本书很系统,自己也想把以前在一些书上看到的分散的东西彻底归整一遍。于是就从这里开个头吧。

    还记得Jeffry Richter在《Appiled .NET Framework Programming》里的那个利用对象复苏设计的那个对象池吗?

    且请容许我把代码在这里再贴一遍:

      1using System;
      2
      3using System.Collections;
      4
      5
      6
      7namespace RichtersObjectPool
      8
      9{
    10
    11       class Expensive
    12
    13       {
    14
    15              static Stack pool = new Stack();
    16
    17
    18
    19              public static Expensive GetObjectFromPool()
    20
    21              {
    22
    23                     return (Expensive) pool.Pop();
    24
    25              }
    26
    27
    28
    29              public static void ShutdownThePool()
    30
    31              {
    32
    33                     pool = null;
    34
    35              }
    36
    37
    38
    39              public Expensive()
    40
    41              {
    42
    43                     //构造对象花费较长时间
    44
    45                     pool.Push(this);
    46
    47              }
    48
    49
    50
    51              ~Expensive()
    52
    53              {
    54
    55                     if (pool != null)
    56
    57                     {
    58
    59                            GC.ReRegisterForFinalize(this);
    60
    61
    62
    63                            pool.Push(this);
    64
    65                     }
    66
    67              }
    68
    69       }
    70
    71
    72
    73       class App
    74
    75       {
    76
    77              [STAThread]
    78
    79              static void Main(string[] args)
    80
    81              {
    82
    83                     for (int i = 0; i < 10; i++)
    84
    85                            new Expensive();
    86
    87
    88
    89                     //一些操作
    90
    91
    92
    93                     Expensive e = Expensive.GetObjectFromPool();
    94
    95
    96
    97                     //使用e
    98
    99
    100
    101                     Expensive.ShutdownThePool();
    102
    103
    104
    105              }
    106
    107       }
    108
    109}
    110
    111
    现在且就这个object pool的实现我们来仔细看看。从整个设计来讲,Expensive类是一个多例模式。它通过一个聚集(静态的Stack)来管理该类的多个实例。从技巧上看,利用的是GC的对象复苏特性,即重载了Finalize方法的类(在C#中即是析构函数)在第一次垃圾收集时会经历一个终止化链表到终止化可达队列的转移的过程,如此从“死亡”既而又获得了“重生”。在这个实现中,在Finalize()中调用GC.ReRegisterForFinalize()是实现的关键。

    但是,这个实现很大的限制了我们的应用。

    我们被限制的应用有哪些呢?

    这个object pool还不够智能化,每次我们在开始运行时,要自己手动构造一些对象,在应用程序退出时,还必须牢记要自己关闭object pool。
    栈中的元素只能增加,不能减少。即每当我再次构造一个对象压栈后,以前栈中的对象就必须多次调用GetObjectFromPool()才能得到。这个似乎不是很方便。
    如果在程序运行期间构造了太多这样的对象,那么势必会耗费很多的资源,而实际上,可能只有在object pool中少数的对象正被使用或经常被使用。在某些场景下,我们需要一种对象生命周期的管理方法。
    最后,最重要的是这个类不是线程安全的。
    首先,我们可以考虑把管理多个对象的数据结构换做线程安全的哈希表:

    在这里我要对HashTable多说两句。《Professinal C# 3rd Edition》里说的很清楚:

    容量为素数的话,工作效率更高,且当散列表扩大容量重新分配内存的时候,总会选择一个素数作为其新的容量。
    负载最大值越小,工作效率越高,但占据内存也越大。
    HaskTable确定两个键A和B是否相等的方式是调用A.Equals(B)。即必须确保如果A.Equals(B)是true,则A.GetHashCode()和B.GetHashCode()必须返回相同的散列。
    上面的第三条也就是为什么编译器会以警告的方式强制必须同时重写Equals()和GetHashCode()的原因。

    对于System.Object来说,Equals()仅仅比较引用,而GetHashCode()会根据对象的地址返回一个散列。因此如果你的类这两个方法都不重载,将其运用到HashTable是可以正常工作的,但这样的类会受到“同一与相等”这个典型问题的限制。因此,最后自己为要用做键的类重写这两个方法。

    此外,MS已经为String提供了一种虽然复杂、但很有效的散列算法。我们可以在自己的实现中利用这个算法。

    最后,一般简单高效率的散列算法的设计是:获取字段,把它们与较大的素数相乘,再把结果加起来。

    第二,既然我们选择了HashTable,那么用什么做主键,什么做值呢?

    还记得,我们提过想把生命周期管理拿进来,而且希望对象的创建和销毁更自动化。鉴于此二者。我们可以把对象本身用做主键,而值用创建该对象时的时间来填充,每当被使用后,该值即立刻被更新。通过一个定时触发器根据对象的最近使用时间。来管理object pool中的对象。

    第三,也就是关于对象的使用问题。其实,本质上讲,是一个有状态和无状态的问题。如果对象是有状态的,当我们从object pool取出一个对象后,该对象的状态不一定符合我们使用的要求。比如一个数据库连接,当我们从object pool取出时,它很有可能是关闭的。因此,这就很有必要在我们的取出操作中进行对象状态的验证。

    第四,同步。除了谈到的使用线程安全的HashTable外,我们还有一些操作是需要原子特性的。我们可以把lock或者monitor施加在critical section上来得到保证。

    说了这么多,我们来看看《C# Threading Handbook》中的这个更具使用价值的object pool的实现:


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2009/9/5 9:06:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 Dot NET,C#,ASP,VB 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/6/15 20:23:31

    本主题贴数4,分页: [1]

     *树形目录 (最近20个回帖) 顶端 
    主题:  多线程:从对象池(object pool)谈同步(syncronization)&nbs..(4957字) - 卷积内核,2009年9月5日
        回复:  Hit Line Source Code 1 using System;..(4996字) - 卷积内核,2009年9月5日
        回复:  我添加的代码如下:private DBConnectionSingleton pool = D..(2384字) - 卷积内核,2009年9月5日
        回复:  作为该书中的一个完整的例子。书中提供了一个数据库连接object pool的实现。代码如下:u..(2700字) - 卷积内核,2009年9月5日

    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    62.500ms