Double Check
在项目开发中,在用WCF讲数据的时候,架构师来了个Double Check,还英语都拼不准的我连单词都不懂,更何况了解了。
下面做了下手记:
Web缓存应用中,缓存更新时,为防止并发的产生。引用多线程开发中的Double Check!
代码如下:
//锁
private static readonly Object locker = new Object();
//获取缓存方法
private String UpdateCache()
{
//第一次判断缓存是否存在
if(Cache["key"] == null)
{
//进入锁
lock(locker)
{
//第二次判断缓存是否存在
if(Cache["key"] == null)
{
//更新缓存
String value = "获取新的缓存值";
Cache.Add("key",value,过期条件);
}
}
}
return Cache["key"] as String;
}
private static readonly Object locker = new Object();
//获取缓存方法
private String UpdateCache()
{
//第一次判断缓存是否存在
if(Cache["key"] == null)
{
//进入锁
lock(locker)
{
//第二次判断缓存是否存在
if(Cache["key"] == null)
{
//更新缓存
String value = "获取新的缓存值";
Cache.Add("key",value,过期条件);
}
}
}
return Cache["key"] as String;
}
为什么要使用Double Check!
因为Web并发是多线程访问的,但缓存是属于同一个。这里就产生了多个线程访问同一内存的共享问题。
没错lock内部的代码只会有一条线程访问,但仍然会有以下2种问题存在。
- 如果每次访问缓存时都把判断缓存是否存在的逻辑放在lock内部的话,等于强制将多线程的系统变成单线程。会大大影响性能。
- 如果只放在外边,会导致如果同时有N条线程访问判断,那么这N条线程可能都会同时通过判断进入lock进行排队,则可能更新缓存的操作会被执行 N次,就会造成运算的冗余。如果缓存的新数据是通过很复杂的数据库查询获得的那么将会连续执行N次这个查询,但其中的N-1次都是重复工作。
所以在Web应用的缓存更新方法中,要在lock内部进行第二次判断才能使代码达到最优,这就是常说的Double Check!