当我们的程序在单一线程运行很正常,但是在多线程下,代码并不是哪么的强键!
我们编码的时候经常在使用缓存(或者使用设计模式的单件模式),缓存的东西一般是常用或者是运行代价非常昂贵的
下面我们看一个例子:
说明:yi.Components.YICache是一个管理缓存的Class
yi.Components. ResourceManager是一个管理公共的资源的Class
yi.Components. ResourceManager下有以下这个方法获取资源信息(这在项目中可能是使用一个XML文件,或者是反序化一个流对象)
private static Hashtable GetResource()
{
String cacheKey = Type.GetType("ResourceManager").ToString();
Hashtable resources = YICache.Get(cacheKey) as Hashtable;
if (null == resources)
{
resources = new Hashtable();
LoadResource(resources); //这里假设做一些非常昂贵的IO操作或者其它操作
YICache.Max(cacheKey, resources);
}
return resources;
}
以上哪一段码在单线程下运行的效果正是我们所想要的结果,哪么在多线程下是否是安全呢?
使用ACT对它进行测试一下,然后再使用监视工具进行监视,其结果并不是我们想要的,
测试的结果的是:平均运行两次就去执行非常昂贵的IO操作或者是其它操作
这时候你可以会想到,可能有ABC三个线程同时对GetResource()方法进行访问,哪么如果同时有ABC同时对LoadResource()这个方法进行操作.问题可已经找到了,哪么你可能认为上个锁不就完事.
然后将以上代码进行更改如下:
public static readonly object CacheObject = new object();
private static Hashtable GetResource()
{
string cacheKey = Type.GetType("ResourceManager").ToString();
Hashtable resources = YICache.Get(cacheKey) as Hashtable;
if (null == resources)
{
lock (CacheObject)
{
resources = new Hashtable();
LoadResource(resources);//这里假设做一些非常昂贵的IO操作或者其它操作
YICache.Max(cacheKey, resources);
}
}
return resources;
}
这代码看起来比第一次的好多了。
但是个假设有两个线程A和B同时对GetResource()方法进行访问,哪么A和B同时到达lock这一行代码,而A先获取锁,然后B只有等待,而当A装载资源完成后,B仍然进行装载资源。
现在对以上代码再进行更改如下:
public static readonly object CacheObject = new object();
private static Hashtable GetResource()
{
string cacheKey = Type.GetType("ResourceManager").ToString();
Hashtable resources = YICache.Get(cacheKey) as Hashtable;
if (null == resources)
{
lock (CacheObject)
{
Hashtable resources = YICache.Get(cacheKey) as Hashtable;
if (null == resources)
{
resources = new Hashtable();
LoadResource(resources);//这里假设做一些非常昂贵的IO操作或者其它操作
YICache.Max(cacheKey, resources);
}
}
}
return resources;
}
这样就可以保证在多线程下对一些昂贵的操作进行比较好的控制。
以上的讨论在实现单件设计模式同样存在这个问题