多线程:从对象池(object pool)谈同步(syncronization) ——一个调试的问题

 

 

 

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

 

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

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

  1 None.gif using  System;
  2 None.gif
  3 None.gif using  System.Collections;
  4 None.gif
  5 None.gif 
  6 None.gif
  7 None.gif namespace  RichtersObjectPool
  8 None.gif
  9 ExpandedBlockStart.gifContractedBlock.gif dot.gif
 10InBlock.gif
 11InBlock.gif       class Expensive
 12InBlock.gif
 13ExpandedSubBlockStart.gifContractedSubBlock.gif       dot.gif{
 14InBlock.gif
 15InBlock.gif              static Stack pool = new Stack();
 16InBlock.gif
 17InBlock.gif 
 18InBlock.gif
 19InBlock.gif              public static Expensive GetObjectFromPool()
 20InBlock.gif
 21ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
 22InBlock.gif
 23InBlock.gif                     return (Expensive) pool.Pop();
 24InBlock.gif
 25ExpandedSubBlockEnd.gif              }

 26InBlock.gif
 27InBlock.gif 
 28InBlock.gif
 29InBlock.gif              public static void ShutdownThePool()
 30InBlock.gif
 31ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
 32InBlock.gif
 33InBlock.gif                     pool = null;
 34InBlock.gif
 35ExpandedSubBlockEnd.gif              }

 36InBlock.gif
 37InBlock.gif 
 38InBlock.gif
 39InBlock.gif              public Expensive()
 40InBlock.gif
 41ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
 42InBlock.gif
 43InBlock.gif                     //构造对象花费较长时间
 44InBlock.gif
 45InBlock.gif                     pool.Push(this);
 46InBlock.gif
 47ExpandedSubBlockEnd.gif              }

 48InBlock.gif
 49InBlock.gif 
 50InBlock.gif
 51InBlock.gif              ~Expensive()
 52InBlock.gif
 53ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
 54InBlock.gif
 55InBlock.gif                     if (pool != null)
 56InBlock.gif
 57ExpandedSubBlockStart.gifContractedSubBlock.gif                     dot.gif{
 58InBlock.gif
 59InBlock.gif                            GC.ReRegisterForFinalize(this);
 60InBlock.gif
 61InBlock.gif 
 62InBlock.gif
 63InBlock.gif                            pool.Push(this);
 64InBlock.gif
 65ExpandedSubBlockEnd.gif                     }

 66InBlock.gif
 67ExpandedSubBlockEnd.gif              }

 68InBlock.gif
 69ExpandedSubBlockEnd.gif       }

 70InBlock.gif
 71InBlock.gif 
 72InBlock.gif
 73InBlock.gif       class App
 74InBlock.gif
 75ExpandedSubBlockStart.gifContractedSubBlock.gif       dot.gif{
 76InBlock.gif
 77InBlock.gif              [STAThread]
 78InBlock.gif
 79InBlock.gif              static void Main(string[] args)
 80InBlock.gif
 81ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
 82InBlock.gif
 83InBlock.gif                     for (int i = 0; i < 10; i++)
 84InBlock.gif
 85InBlock.gif                            new Expensive();
 86InBlock.gif
 87InBlock.gif 
 88InBlock.gif
 89InBlock.gif                     //一些操作
 90InBlock.gif
 91InBlock.gif 
 92InBlock.gif
 93InBlock.gif                     Expensive e = Expensive.GetObjectFromPool();
 94InBlock.gif
 95InBlock.gif 
 96InBlock.gif
 97InBlock.gif                     //使用e
 98InBlock.gif
 99InBlock.gif 
100InBlock.gif
101InBlock.gif                     Expensive.ShutdownThePool();
102InBlock.gif
103InBlock.gif 
104InBlock.gif
105ExpandedSubBlockEnd.gif              }

106InBlock.gif
107ExpandedSubBlockEnd.gif       }

108InBlock.gif
109ExpandedBlockEnd.gif}

110 None.gif
111 None.gif

 

现在且就这个object pool的实现我们来仔细看看。从整个设计来讲,Expensive类是一个多例模式。它通过一个聚集(静态的Stack)来管理该类的多个实例。从技巧上看,利用的是GC的对象复苏特性,即重载了Finalize方法的类(在C#中即是析构函数)在第一次垃圾收集时会经历一个终止化链表到终止化可达队列的转移的过程,如此从“死亡”既而又获得了“重生”。在这个实现中,在Finalize()中调用GC.ReRegisterForFinalize()是实现的关键。

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

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

Ø         这个object pool还不够智能化,每次我们在开始运行时,要自己手动构造一些对象,在应用程序退出时,还必须牢记要自己关闭object pool

Ø         栈中的元素只能增加,不能减少。即每当我再次构造一个对象压栈后,以前栈中的对象就必须多次调用GetObjectFromPool()才能得到。这个似乎不是很方便。

Ø         如果在程序运行期间构造了太多这样的对象,那么势必会耗费很多的资源,而实际上,可能只有在object pool中少数的对象正被使用或经常被使用。在某些场景下,我们需要一种对象生命周期的管理方法。

Ø         最后,最重要的是这个类不是线程安全的。

 

首先,我们可以考虑把管理多个对象的数据结构换做线程安全的哈希表:

 

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

Ø         容量为素数的话,工作效率更高,且当散列表扩大容量重新分配内存的时候,总会选择一个素数作为其新的容量。

Ø         负载最大值越小,工作效率越高,但占据内存也越大。

Ø         HaskTable确定两个键AB是否相等的方式是调用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的实现:

  

作为该书中的一个完整的例子。书中提供了一个数据库连接object pool的实现。代码如下:

None.gif using  System;
None.gif
None.gif
using  System.Data.SqlClient;
None.gif
None.gif 
None.gif
None.gif
namespace  WroxCS
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
InBlock.gif     
public sealed class DBConnectionSingleton : ObjectPool
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif     
dot.gif{
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif       
private DBConnectionSingleton() dot.gif{}
InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif       
public static readonly DBConnectionSingleton Instance =
InBlock.gif
InBlock.gif           
new DBConnectionSingleton();
InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif       
private static string _connectionString =
InBlock.gif
InBlock.gif           
@"server=(local); Integrated Security=SSPI;database=northwind";
InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif       
public static string ConnectionString
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif       
dot.gif{
InBlock.gif
InBlock.gif         
set
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif         
dot.gif{
InBlock.gif
InBlock.gif           _connectionString 
= value;
InBlock.gif
ExpandedSubBlockEnd.gif         }

InBlock.gif
InBlock.gif         
get
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif         
dot.gif{
InBlock.gif
InBlock.gif           
return _connectionString;
InBlock.gif
ExpandedSubBlockEnd.gif         }

InBlock.gif
ExpandedSubBlockEnd.gif       }

InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif       
protected override object Create()
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif       
dot.gif{
InBlock.gif
InBlock.gif         SqlConnection temp 
= new SqlConnection( ConnectionString);
InBlock.gif
InBlock.gif         temp.Open();
InBlock.gif
InBlock.gif         
return(temp);
InBlock.gif
ExpandedSubBlockEnd.gif       }

InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif       
protected override bool Validate(object o)
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif       
dot.gif{
InBlock.gif
InBlock.gif         
try
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif         
dot.gif{
InBlock.gif
InBlock.gif           SqlConnection temp 
= (SqlConnection)o;
InBlock.gif
InBlock.gif          
return(
InBlock.gif
InBlock.gif            
! ((temp.State.Equals(System.Data.ConnectionState.Closed))));
InBlock.gif
ExpandedSubBlockEnd.gif         }

InBlock.gif
InBlock.gif         
catch (SqlException)
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif         
dot.gif{
InBlock.gif
InBlock.gif           
return false;
InBlock.gif
ExpandedSubBlockEnd.gif         }

InBlock.gif
ExpandedSubBlockEnd.gif       }

InBlock.gif
InBlock.gif 
InBlock.gif
InBlock.gif       
protected override void Expire(object o)
InBlock.gif
ExpandedSubBlockStart.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值