技术笔记:Delphi多线程应用读写锁
在多线程应用中锁是一个很简单又很复杂的技术,之所以要用到锁是因为在多进程/线程环境下,一段代码可能会被同时访问到,如果这段代码涉及到了共享资源(数据)就需要保证数据的正确性。也就是所谓的线程安全。之前写过一篇着于Java线程安全的博客:链接
我是在写一个服务端程序时应用到读写锁,在一个内存缓存。先来看看排斥锁的写法,代码如下:
function TValueCalc.GetValue(const key: string): TCache; var objCache: TCache; begin Result := nil; //加锁 FRead2Lock.Enter; try objCache := GetCacheInstance.GetCache(sKey); if objCache <> nil then begin //缓存正在更新,直接退出,不让线程等待,以提高性能 if objCache.IsUpdating then Exit; //是否过期,未过期直接返回 if MilliSecondsBetween(Now, objCache.LastTime) < Expires then begin Result := objCache; Exit; end; objCache.IsUpdating := True; end; //更新缓存 Result := UpdateCache(key); finally //释放锁 FRead2Lock.Leave; end; end;
但是这个缓存有一个特点,就是每个缓存项存活的时间很短,这就会导致大量的缓存更新操作,而这些更新操作由于业务不同耗时也不同。这就导致线程都在等待缓存的更新。为了解决这个问题引入了读写锁。让读锁可以在写数据时释放,让后面的线程继续执行查找缓存数据。
function TValueCalc.GetValue(const key: string): TCache; var objCache: TCache; begin Result := nil; FRead2Lock.Enter; try objCache := GetCacheInstance.GetCache(sKey); if objCache <> nil then begin //缓存正在更新,直接退出,不让线程等待,以提高性能 if objCache.IsUpdating then Exit; //检查缓存是否过期 if MilliSecondsBetween(Now, objCache.LastTime) < Expires then begin Result := objCache; Exit; end; objCache.IsUpdating := True; end; //先释放读锁,后面线程可以继续读数据 FRead2Lock.Leave; //加写锁 FWriteLock.Enter; try //更新缓存 Result := UpdateCache(key); finally //数据更新完毕,重新加上读锁 FRead2Lock.Enter; //释放写锁 FWriteLock.Leave; end; finally //释放读锁 FRead2Lock.Leave; end; end;
读写锁是在进行写数据前先释放掉读锁,然后马上加上写锁,这样后续读缓存的线程就可以继续执行,不会等待。而同时写缓存已经上锁,这样就不会资源冲突。
读写锁这样就可以大大提升读缓存的性能,也不会影响到缓存的更新了。
注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错请点击下右下角的推荐,非常感谢!