技术笔记: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;

读写锁是在进行写数据前先释放掉读锁,然后马上加上写锁,这样后续读缓存的线程就可以继续执行,不会等待。而同时写缓存已经上锁,这样就不会资源冲突。

 

读写锁这样就可以大大提升读缓存的性能,也不会影响到缓存的更新了。

 

posted @ 2016-01-15 17:29  5207  阅读(2416)  评论(0编辑  收藏  举报