对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. cmd/powershell常用命令 git常用命令

    cmd/powershell: 1. 新建文件夹: mkdir directoryName 2. 新建文件: cmd: type nul>fileName (空文件) powershell: n ...

  2. 第六章 SSH远程服务介绍

    一.相关介绍 1.简介SSH是一个安全协议,在进行数据传输时,会对数据包进行加密处理,加密后在进行数据传输.确保了数据传输安全.那SSH服务主要功能有哪些呢? 1)提供远程连接的服务 linux远程连 ...

  3. JavaSE学习笔记02运算符、帮助文档生成与Scanner输入

    1. 运算符 1. 算术运算符:+,-,*,/,%,++,-- //二元运算符 int a = 10; int b = 20; int c = 25; int d = 25; System.out.p ...

  4. Linux命令行bash的快捷键

    提升效率 锁屏 Ctrl + s 敲什么命令没反应,但是敲上去了,屏幕上不做任何反应 Ctrl + q 再解锁 例如: 先Ctrl + s 锁屏 然后在命令行敲入 [root@C8-1 ~]# rm ...

  5. npm install各种命令模式

    npm install 几种命令模式: npm install moduleName 安装模块到项目目录下 npm install -g moduleName npm install -g 将模块安装 ...

  6. Codeforces Round #678 (Div. 2)

    Codeforces Round #678 (Div. 2) A. Reorder 题意:有一个有 n 个数的序列 a ,以及一个数 m ,问能否给序列a重新排序,能够满足式子 $\sum_{i=1} ...

  7. 自定义Jackson2HttpMessageConverter,适应.html后缀url

    Jackson2HttpMessageConverter 用处 SpringMVC中,controller中的方法返回java Bean对象,mvc将此对象转换成字符串 默认支持的mediaType: ...

  8. 如何在 vue 项目里正确地引用 jquery

    转载 2016年11月13日 使用vue-cli构建的vue项目,webpack的配置文件是分散在很多地方的,而我们需要修改的是build/webpack.base.conf.js,修改两处的代码 / ...

  9. 多快好省地使用pandas分析大型数据集

    1 简介 pandas虽然是个非常流行的数据分析利器,但很多朋友在使用pandas处理较大规模的数据集的时候经常会反映pandas运算"慢",且内存开销"大". ...

  10. sqlsugar入门(2)-C#方法与sugar自定义函数的区别

    1.使用tostring获取当天数据 var list = ssc.Queryable<Student>().Where(o => o.CreateTime.Value.ToStri ...