对Monitor的使用可以防止lock的时间过长并且可以设置其对应的超时时间达到对预期代码的一个控制,合理的使用timeout可以有助于程序的健壮性。但是对于不同的并发程序可能某些时候我们需要的粒度是不一样的,从而产生的一个问题是需要更细粒度的锁来保证,又因为默认的字符串无法共享导致的无法通过string来进行锁粒度的细分所以可能需要自己重写一个锁来保证到达更细粒度的控制,可能这时候有人要说不是有string.Intern可以进行字符串保留,达到类似的效果,对于这个我只想说内部确定的字符串确实可以达到这个效果,但是因为字符串保留机制就导致了gc不会对此进行回收,会导致如果外部输入的string是不可控的情况下就可以给程序造成诸如oom的问题,对此我抛砖引玉希望各位博友可以提出宝贵的意见或者建议,或者有现成的更好的解决方案也请分享一下,下面贴代码:

    public class MonitorStr
{
private MonitorStr() { }
private static ConcurrentDictionary<string, MonitorStrEntry> _lockDics = new ConcurrentDictionary<string, MonitorStrEntry>();
private const int _concurrentCount = 31;
private static object[] _lockers = new object[_concurrentCount]; static MonitorStr()
{
for (int i = 0; i < _concurrentCount; i++)
{
_lockers[i] = new object();
}
}
private static int GetIndex(string key)
{
return Math.Abs(key.GetHashCode() % _concurrentCount);
}
public static bool TryEnter(string key, int timeoutMillis)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key));
MonitorStrEntry entry = null;
var locker = _lockers[GetIndex(key)];
lock (locker)
{
if (!_lockDics.TryGetValue(key, out entry))
{
entry = new MonitorStrEntry();
_lockDics[key] = entry;
}
entry.Increment();
} var acquired = Monitor.TryEnter(entry, timeoutMillis);
if (!acquired)
entry.Decrement();
return acquired;
} public static void Exit(string key)
{
var entry = _lockDics[key];
Monitor.Exit(entry);
if (entry.Decrement() == 0)
{
var locker = _lockers[GetIndex(key)];
lock (locker)
{
if (entry.CanRemove())
{
Console.WriteLine(key + "remove");
_lockDics.TryRemove(key, out var v);
}
}
}
}
class MonitorStrEntry
{
private int _lockCount; public int Increment()
{
Interlocked.Increment(ref _lockCount);
return _lockCount;
} public int Decrement()
{
Interlocked.Decrement(ref _lockCount);
return _lockCount;
}
public bool CanRemove()
{
return _lockCount == 0;
}
} }

这个代码的原理就是利用数组对象锁和传入的string key进行对不同key之间的粒度的区分,因为不同的key之间的hashcode不一致所以对取到的锁对象locker也不一样,达到降低锁并发的级别,字典存储的entry内部维护一个锁的加锁次数达,利用cas保证并发多线程安全且高效。

如何使用

            var key = "testKey";
var timeoutMillis = 3000;
var acquired = false;
try
{ acquired = MonitorStr.TryEnter(key, timeoutMillis);
if (acquired)
{
//Do Something
}
else
{
throw new XXXTimeOutException();
}
}
finally
{
MonitorStr.Exit(key);
}

哪个场景下可以使用呢?

            //使用场景,诸如多线程查数据库环境的情况下
var userId = "xxxx";
var user = Cache.Query(userId);
if (user == null)
{
var acquired = false;
try
{ acquired = MonitorStr.TryEnter(key, timeoutMillis);
if (acquired)
{
//Do Something
user = Cache.Query(userId);
if (user == null)
{
user = DB.Query(userId);
}
}
else
{
throw new XXXTimeOutException();
}
}
finally
{
MonitorStr.Exit(key);
}
}

谢谢

Monitor的扩展支持string的超时锁的更多相关文章

  1. openerp学习笔记 domain 增加扩展支持,例如支持 <field name="domain">[('type','=','get_user_ht_type()')]</field>

    示例代码1,ir_action_window.read : # -*- coding: utf-8 -*-from openerp.osv import fields,osv class res_us ...

  2. 如何基于String实现同步锁?

    在某些时候,我们可能想基于字符串做一些事情,比如:针对同一用户的并发同步操作,使用锁字符串的方式实现比较合理.因为只有在相同字符串的情况下,并发操作才是不被允许的.而如果我们不分青红皂白直接全部加锁, ...

  3. wrk 及扩展支持 tcp 字节流协议压测

    wrk 及扩展支持 tcp 字节流协议压测 高性能.方便使用的 HTTP(s) 的流量压测工具,结合了多个开源项目开发而成: redis 的 ae 事件框架 luajit openssl http-p ...

  4. 如何基于 String 实现同步锁?

    如何基于String实现同步锁? 在某些时候,我们可能想基于字符串做一些事情,比如:针对同一用户的并发同步操作,使用锁字符串的方式实现比较合理. 因为只有在相同字符串的情况下,并发操作才是不被允许的. ...

  5. //四舍五入//得到倒序//比较字符串//拦截时间,实现超时锁屏//判断是否越狱//配置PodFile//Storyboard中跳转操作//处理不可逆的push界面操作

    //处理不可逆的push界面操作 VerifyRealNameViewController *verifyRealNameCtrl = [VerifyRealNameViewController vi ...

  6. ExtJs4 笔记(2) ExtJs对js基本语法扩展支持

    本篇主要介绍一下ExtJs对JS基本语法的扩展支持,包括动态加载.类的封装等. 一.动态引用加载 ExtJs有庞大的类型库,很多类可能在当前的页面根本不会用到,我们可以引入动态加载的概念来即用即取.这 ...

  7. 安装php的memcached模块和扩展支持sasl

    memcached的1.2.4及以上增加了CAS(Check and Set)协议,对于同一key的多进行程的并发处理问题.这种情况其实根数据库很像,如果同时有几个进程对同一个表的同一数据进行更新的话 ...

  8. ExtJs对js基本语法扩展支持

    ExtJs对js基本语法扩展支持 本篇主要介绍一下ExtJs对JS基本语法的扩展支持,包括动态加载.类的封装等. 一.动态引用加载 ExtJs有庞大的类型库,很多类可能在当前的页面根本不会用到,我们可 ...

  9. [转载]ExtJs4 笔记(2) ExtJs对js基本语法扩展支持

    作者:李盼(Lipan)出处:[Lipan] (http://www.cnblogs.com/lipan/) 本篇主要介绍一下ExtJs对JS基本语法的扩展支持,包括动态加载.类的封装等. 一.动态引 ...

随机推荐

  1. php监控文件变化

    <?php $process = new \Swoole\Process(function(){ $filename = "a.conf"; $md5file = md5_f ...

  2. Linux 生产主流版本

    CentOS 8 下载 https://mirrors.aliyun.com/centos/8.1.1911/isos/x86_64/CentOS-8.1.1911-x86_64-dvd1.iso L ...

  3. vue知识点15

    1.回调地狱的三种方案:函数    promise     async await          2. 子组件与子组件之间的传递: 可以借用公共父元素.子组件A  this.$emit(" ...

  4. Sentinel流控规则

    流控规则 注:Sentinel的监控页面一开始是没有东西,需要对监控的服务发起请求后才会出现 资源名:唯一名称,默认请求路径 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个 ...

  5. App在后台运行时如何保存数据到sqlite数据库

    iOS程序进入后台后,是不允许读写任何文件和数据库(sqlite),但是允许读写NSUserDefault中的数据. 因此在后台时如果想存储数据,则可使用NSUserDefault(偏好设置)临时保存 ...

  6. springboot错误统一处理

    1,对于404,500这类错误,可以直接新建public/error目录 ,在error目录 中新建404.html, 500.html或5xx.html,springboot会自动跳转到这些静态页面 ...

  7. Java 内存级默认DNS缓存

    Java 默认的DNS缓存时间,即不设置任一系统属性,如networkaddress.cache.ttl 设置SecurityManager 默认的CachePolicy为Forever,即永久缓存D ...

  8. DOM0级事件误区-addEventListener

    百度上很多篇文章讲解addEventListener DOM0级事件的时候讲解的都是覆盖 概念如下: 同一个元素的同一种事件只能绑定一个函数,否则后面的函数会覆盖之前的函数 其实不然,官方讲解:添加的 ...

  9. D. Tavas and Malekas 解析(字串匹配)

    Codeforce 535 D. Tavas and Malekas 解析(字串匹配) 今天我們來看看CF535D 題目連結 題目 給你一個字串$p$和一些$index$代表字串$p$在哪些位置會和長 ...

  10. 在 k8S 中搭建 SonarQube 7.4.9 版本(使用 PostgreSQL 数据库)

    搭建 SonarQube 和 PostgreSQL 服务 本文搭建的 SonarQube 版本是 7.4.9-community,由于在官方文档中声明 7.9 版本之后就不再支持使用 MySQL 数据 ...