对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. 第十章 nginx常用配置介绍

    一.虚拟主机 1.配置方式 #虚拟主机配置方式:1.基于多IP的方式2.基于多端口的方式3.基于多域名的方式 2.方式一:基于多IP的方式 1.第一个配置文件[root@web02 /etc/ngin ...

  2. Helium文档15-WebUI自动化-chromedriver问题

    前言 helium库是自带chromedriver的,我们怎么来查看在哪里呢? 目录介绍 用我的电脑上的路径打比方如下: D:\Program Files (x86)\Python38\Lib\sit ...

  3. Linux入门到放弃之七《进程管理》

    进程管理 1.查看所有进程,并用全格式显示: 命令:ps -ef 2.用ps命令查看系统当前的进程,并把系统当前的进程保存到文件process中: 命令:ps aux >> process ...

  4. Hive sql函数

    date: 2018-11-16 19:03:08 updated: 2018-11-16 19:03:08 Hive sql函数 一.关系运算 等值比较: = select 1 from dual ...

  5. python的各种库的用法

    scipy.io 用于输入和输出数据的操作,可操作matlab的.mat文件. (1)加载.mat文件的数据 import scipy.io as sci data_dir = sci.loadmat ...

  6. C2. Pokémon Army (hard version) 解析(思維)

    Codeforce 1420 C2. Pokémon Army (hard version) 解析(思維) 今天我們來看看CF1420C2 題目連結 題目 略,請直接看原題. 前言 根本想不到這個等價 ...

  7. 谈谈nginx和lvs各自的优缺点以及使用

            在最开始呢,咱们先说一下什么叫负载均衡,负载均衡呢,就是将一批请求,根据请求的内容,分发到不同的后端去进行相应的处理,从而提供负载分担,主备切换等功能.                 ...

  8. 群晖DS218+部署mysql

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  9. OpenCascade拓扑对象之:TopoDS_Shape的三要素

    @font-face { font-family: "Times New Roman" } @font-face { font-family: "宋体" } @ ...

  10. Jmeter 获取系统时间

    ${__time(yyyy-MM-dd HH:mm:ss:SSS,time)} :格式化生成时间格式 2020-11-03 21:59:13:658