对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. Java Map转成xml标签字符串

    一个简单的java实现,供参考: package com.trilogy.session.data; import java.lang.reflect.Field; import java.util. ...

  2. Visual Studio2015 、2017中如何支持MYSQL数据源(转)

    转至:https://blog.csdn.net/ght886/article/details/80902457 Visual Studio默认只显示微软自己的SQL Server数据源,如下图所示: ...

  3. 如果只推荐一本 Python 书,我要 Pick 它!

    今年二月初,我偶然看到了一条推特: <流畅的Python>一书的作者发布了一条激动人心的消息:他正在写作第二版! 如果要票选最佳的 Python 进阶类书目,这本书肯定会是得票率最高的书籍 ...

  4. 2020-2021-1 20209306 《linux内核原理与分析》第二周作业

    一.实验一内容及分析 1.实验一内容过程截图 2.实验一完成后收获 可以看到汇编代码中出现了eax.esp.ebp.eax是累加寄存器,esp是堆栈指针寄存器,ebp是基指针寄存器.汇编代码中用到了m ...

  5. Redis---07主从复制(哨兵模式)

    一.什么是哨兵模式 基于主从复制的一般模式(一主二从)下,当发生主机发生宕机时,会通过流言协议判断主机是不是宕机,是的话则会通过投票协议自动把某一个从机转换成主机. 二.设置哨兵模式的配置文件 通过r ...

  6. 【问题记录】—.NetCore 编译问题

    最近在协助验证Jenkins自动编译发布时,对一些.Net Core编译问题进行了解决:特记录一下 一.编译生成netcoreapp目录问题 问题现象 .net core项目编译输出目录总是包含在[n ...

  7. 【转】Liunx常用命令详解

    Liuux命令查询入口 Linux命令 - 系统信息 命令代码 注释说明 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 ...

  8. Luogu P4234 最小差值生成树

    题意 给定一个 \(n\) 个点 \(m\) 条边的有权无向图,求出原图的一棵生成树使得该树上最大边权与最小边权的差值最小. \(\texttt{Data Range:}1\leq n\leq 5\t ...

  9. 模块导入from collections import Iterator,Iterable失败

    1.引入模块报错 from collections import Iterator,Iterable 报错: DeprecationWarning: Using or importing the AB ...

  10. Python爬虫之线程池

    详情点我跳转 关注公众号"轻松学编程"了解更多. 一.为什么要使用线程池? 对于任务数量不断增加的程序,每有一个任务就生成一个线程,最终会导致线程数量的失控,例如,整站爬虫,假设初 ...