zoukankan      html  css  js  c++  java
  • Monitor的扩展支持string的超时锁

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

        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
                {
                    if(acquired)
                        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
                    {
                        if(acquired)
                            MonitorStr.Exit(key);
                    }
                }    

    谢谢

  • 相关阅读:
    MS CRM 2011的自定义和开发(10)——CRM web服务介绍(第一部分)——IDiscoveryService
    MS CRM 2011的自定义和开发(7)——视图编辑器(第二部分)
    MS CRM 2011 SDK 5.06版本已经发布
    MS CRM 2011的自定义和开发(11)——插件(plugin)开发(一)
    近来遇到的MS CRM 2011方面的几个问题
    MS CRM 2011的自定义与开发(6)——表单编辑器(第二部分)
    Microsoft Dynamics CRM 2011中,Lookup字段的赋值
    MS CRM 2011的自定义和开发(6)——表单编辑器(第三部分)
    Visual Studio 目标框架造成 命名空间“Microsoft”中不存在类型或命名空间名称“Crm”。是否缺少程序集引用中错误的处理
    一步步学习Reporting Services(二) 在报表中使用简单的参数作为查询条件
  • 原文地址:https://www.cnblogs.com/xuejiaming/p/14022738.html
Copyright © 2011-2022 走看看