关于ManualResetHandle和AutoResetHandle这两个东西的介绍我看就免了吧,用WaitOne()阻塞线程,用Set()来通知阻塞。网上的帖子很多很多,大可baidu或者google一下,还需要弄清楚他们的区别。值得一提的是他们的祖辈WaitHandle里面的方法:WaitAll(),WaitAny()等等这样的静态方法来控制线程的走向,很是得心应手的。
还是先来个实例,在某些情况下我们需要数据库缓存,比如需要频繁的查找查某个表,而对他的操作却不多(比如说论坛里的分级类别),这样的话一次一次的查询数据库,性能上的损失的补偿失。这些东西要是能维护在内存中就是比较好了,而现在的题目就是如何设计这样的数据缓存。
初始的算法大家都会想到,在查询数据库之前先查询缓存中是否有相应的数据,没有再查询数据库。然而WaitHandle里面的WaitAny()却为我们提供了额外的灵感,如果让查询缓存和查询数据库同时进行,那个先得出结果,先得到缓存的返回值,就使用缓存中的数据,要么就拿数据库的数据。这样的效率又比第一种算法优化一些。
具体如何实现呢:
{
//创建缓存线程生命两个ManualResetEvent
//创建缓存线程
Thread threadCache = new Thread(ExecuteSelectWithCache);
//创建数据查询线程
Thread threadDatabase = new Thread(ExecuteSelectWithDatabase);
//启动两个线程
threadDatabase.Start(bagDatabase);
threadCache.Start(bagCache);
//等待任意一个返回
WaitHandle.WaitAny(Events);
//判断返回
//如果数据查询出来则取消缓存查询,返回数据库数据
threadCache.Abort();
return bagDatabase.Dataset;
//如果缓存查询出来则取消数据查询,返回缓存中数据
threadDatabase.Abort();
return bagCache.Dataset;
}
public void ExecuteSelectWithCache(Object obj)
{
//从缓存中查询数据
((DataBag)obj).Event.Set();
}
public void ExecuteSelectWithDatabase(Object obj)
{
//从数据库中查询数据
((DataBag)obj).Event.Set();
}
这样的好处,显儿意见得,两个线程同时来执行,谁快用谁的结果,也就省去了等待缓存查找结束后再来去查找数据库,我们把一个串行的结构变成了并行的结构。而这种线程阻塞和执行的核心在于WaitHandle.WaitAny().
来看看偶乱画的流程图:
OK,我写这么的多东西,只是说明以下ManualResetEvent如何应用到实践中去,另外也说明了一种算法,很多时候,我们程序里面需要并行做事,而不仅仅是一条线下来。也就是说这样的算法不仅仅用在数据缓存中。
}
}
PS:这仅仅是一个玩具而已,实际应用还需要很多改进的地方,缓存的策略,过期的时间,以及如何更加安全,等等。