前两天总结了Lock(Monitor)和Mutex这样的锁,现在继续总结EventWaitHandle。呵呵,各位看客们注意了,我写的东西只是总结的经验想和大家分享,没有教人的意思,写的不对的地方还望大家指正。
前两天总结了Lock(Monitor)和Mutex这样的锁,现在继续总结EventWaitHandle。呵呵,各位看客们注意了,我写的东西只是总结的经验想和大家分享,没有教人的意思,写的不对的地方还望大家指正。
关于ManualResetHandle和AutoResetHandle这两个东西的介绍我看就免了吧,用WaitOne()阻塞线程,用Set()来通知阻塞。网上的帖子很多很多,大可baidu或者google一下,还需要弄清楚他们的区别。值得一提的是他们的祖辈WaitHandle里面的方法:WaitAll(),WaitAny()等等这样的静态方法来控制线程的走向,很是得心应手的。
还是先来个实例,在某些情况下我们需要数据库缓存,比如需要频繁的查找查某个表,而对他的操作却不多(比如说论坛里的分级类别),这样的话一次一次的查询数据库,性能上的损失的补偿失。这些东西要是能维护在内存中就是比较好了,而现在的题目就是如何设计这样的数据缓存。
初始的算法大家都会想到,在查询数据库之前先查询缓存中是否有相应的数据,没有再查询数据库。然而WaitHandle里面的WaitAny()却为我们提供了额外的灵感,如果让查询缓存和查询数据库同时进行,那个先得出结果,先得到缓存的返回值,就使用缓存中的数据,要么就拿数据库的数据。这样的效率又比第一种算法优化一些。
具体如何实现呢:
public DataSet ExecuteSelect(string sql)
{
//创建缓存线程生命两个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如何应用到实践中去,另外也说明了一种算法,很多时候,我们程序里面需要并行做事,而不仅仅是一条线下来。也就是说这样的算法不仅仅用在数据缓存中。
数据缓存
public class DataCache
{
private static Cache m_cache;
public static Cache Cache
{
get
{
if (HttpRuntime.AppDomainAppId == null)
{
HttpRuntime httpRuntime = new HttpRuntime();
}
return HttpRuntime.Cache;
}
set { DataCache.m_cache = value; }
}
public DataSet ExecuteSelect(string sql)
{
ManualResetEvent[] Events = new ManualResetEvent[2];
Events[0] = new ManualResetEvent(false);
Events[1] = new ManualResetEvent(false);
//创建缓存线程
Thread threadCache = new Thread(ExecuteSelectWithCache);
DataBag bagCache = new DataBag();
bagCache.Event = Events[0];
bagCache.Sql = sql;
//创建数据查询线程
Thread threadDatabase = new Thread(ExecuteSelectWithDatabase);
DataBag bagDatabase = new DataBag();
bagDatabase.Event = Events[1];
bagDatabase.Sql = sql;
//启动两个线程
threadDatabase.Start(bagDatabase);
threadCache.Start(bagCache);
//等待任意一个返回
WaitHandle.WaitAny(Events);
//Thread.Sleep(10000);
if (bagDatabase.Exception != null)
throw bagDatabase.Exception;
//判断返回
if (bagDatabase.Dataset.Tables.Count > 0)
{
//如果数据查询出来则取消缓存查询,返回数据库数据
threadCache.Abort();
return bagDatabase.Dataset;
}
else if (bagCache.Dataset.Tables.Count > 0)
{
//如果缓存查询出来则取消数据查询,返回缓存中数据
threadDatabase.Abort();
return bagCache.Dataset;
}
return new DataSet();
}
//从缓存中查询数据
public void ExecuteSelectWithCache(Object obj)
{
try
{
DataBag bag = (DataBag)obj;
if (DataCache.Cache[bag.Sql] != null)
{
bag.Dataset = (DataSet)DataCache.Cache[bag.Sql];
System.Diagnostics.Debug.WriteLine("Get from Cache");
}
else
{
return;
}
}
catch (Exception e)
{
((DataBag)obj).Exception = e;
Console.WriteLine(e);
}
finally
{
DataBag bag = (DataBag)obj;
if (bag.Dataset.Tables.Count > 0 && bag.Exception != null)
{
bag.Event.Set();
}
}
}
//从数据库中查询数据
public void ExecuteSelectWithDatabase(Object obj)
{
try
{
DataBag bag = (DataBag)obj;
using (SqlConnection conn = new SqlConnection("server=.;uid=sa;pwd=123;database=northwind;"))
{
SqlDataAdapter da = new SqlDataAdapter(bag.Sql, conn);
da.Fill(bag.Dataset);
}
DataCache.Cache.Insert(bag.Sql, bag.Dataset);
}
catch (Exception e)
{
((DataBag)obj).Exception = e;
Console.WriteLine(e);
}
finally
{
((DataBag)obj).Event.Set();
}
}
}
public class DataBag
{
private DataSet m_Dataset = new DataSet();
public DataSet Dataset
{
get { return m_Dataset; }
set { m_Dataset = value; }
}
private ManualResetEvent m_Event;
public ManualResetEvent Event
{
get { return m_Event; }
set { m_Event = value; }
}
private String m_sql;
public String Sql
{
get { return m_sql; }
set { m_sql = value; }
}
private Exception m_exception;
public Exception Exception
{
get { return m_exception; }
set { m_exception = value; }
}
}
PS:这仅仅是一个玩具而已,实际应用还需要很多改进的地方,缓存的策略,过期的时间,以及如何更加安全,等等。