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

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏林德熙的博客

WPF 插拔触摸设备触摸失效

最近使用 WPF 程序,在不停插拔触摸设备会让 WPF 程序触摸失效。通过分析 WPF 源代码可以找到 WPF 触摸失效的原因。

1801
来自专栏大内老A

ASP.NET Core中如影随形的”依赖注入”[上]: 从两个不同的ServiceProvider说起

我们一致在说 ASP.NET Core广泛地使用到了依赖注入,通过前面两个系列的介绍,相信读者朋友已经体会到了这一点。由于前面两章已经涵盖了依赖注入在管道构建过...

4087
来自专栏Linyb极客之路

并发编程之读写锁

一、读写锁 ReadWriteLock 读写锁维护了一对相关的锁,一个用于只读操作,一个用于写入操作。只要没有writer,读取锁可以由多个reader线程同...

8945
来自专栏从零开始学自动化测试

python接口自动化8-参数化

前言 前面一篇实现了参数的关联,那种只是记流水账的完成功能,不便于维护,也没什么可读性,接下来这篇可以把每一个动作写成一个函数,这样更方便了。 参数化的思维只...

3286
来自专栏青青天空树

python网络爬虫--简单爬取糗事百科

  具体步骤是这样的:首先查看糗事百科的url:http://www.qiushibaike.com/8hr/page/2/?s=4959489,可以发现pag...

1021
来自专栏软件工程师成长笔记

IE、FireFox、Chrome浏览器中关于URL传参中文乱码,解决兼容性问题!

前台用url传值中文,后台用request.getParameter接收参数。在Firefox,Chrome等浏览器中没有问题。但用IE浏览器就又会出现参数中文...

4302
来自专栏cs

python爬虫的东西

1183
来自专栏AI深度学习求索

Pytorch学习-如何接受命令行参数argparse模块

使用argparse 模块定义解析命令行参数,命令 行参数其实也是应用在程序中的参数,只是为了更方便他人使用程序而设置。

4363
来自专栏Java面试通关手册

Java多线程学习(四)等待/通知(wait/notify)机制

我自己总结的Java学习的系统知识点以及面试问题,目前已经开源,会一直完善下去,欢迎建议和指导欢迎Star: https://github.com/Snailc...

2143
来自专栏安恒网络空间安全讲武堂

MS08-067漏洞调试分析详解

MS08-067漏洞调试分析详解 一、前言 在《Metasploit渗透测试魔鬼训练营》中有对MS08-067漏洞原理的分析,不过作者的文笔十分晦涩难懂,读起来...

34210

扫码关注云+社区

领取腾讯云代金券